nibb_core/snippets/
snippet.rs

1use std::fmt;
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use slug::slugify;
5use crate::snippets::file_type::FileType;
6/// Metadata associated with a snippet.
7///
8/// Includes name, description, tags, programming language, creation and modification timestamps,
9/// as well as visibility status (e.g., public, private).
10#[derive(Debug, Serialize, Deserialize, Clone)]
11pub struct Meta {
12    /// Name of the snippet (used for display and slug generation).
13    pub name: String,
14    /// Short description of the snippet's purpose or context.
15    pub description: String,
16    /// Tags associated with the snippet, e.g. `["sql", "orm", "diesel"]`.
17    pub tags: Vec<String>,
18    /// Language or file type of the snippet (e.g. `rust`, `bash`, `sql`).
19    pub language: FileType,
20    /// Timestamp indicating when the snippet was created (UTC).
21    pub created: DateTime<Utc>,
22    /// Timestamp indicating when the snippet was last modified (UTC).
23    pub modified: DateTime<Utc>,
24    /// Visibility status (e.g. `Private`, `Public`, `Archived`). Defaults to `Private`.
25    #[serde(default = "Visibility::default")]
26    pub visibility: Visibility,
27}
28
29impl Meta {
30    /// Creates a new `Meta` object with the current timestamp for `created` and `modified`.
31    ///
32    /// If no visibility is provided, defaults to `Private`.
33    pub fn new(
34        name: String,
35        description: String,
36        tags: Vec<String>,
37        language: FileType,
38        visibility: Option<Visibility>) -> Self {
39        Self {
40            name,
41            description,
42            tags,
43            language,
44            created: Utc::now(),
45            modified: Utc::now(),
46            visibility: visibility.unwrap_or_default(),
47        }
48    }
49    /// Returns the standard file extension (without a dot) for the snippet's language.
50    pub fn get_content_extension(&self) -> String {
51        self.language.extension().to_string()
52    }
53    /// Returns a slugified version of the snippet's name.
54    pub fn get_slug(&self) -> String {
55        slugify(self.name.as_str())
56    }
57}
58
59
60
61
62impl Default for Visibility {
63    fn default() -> Self {
64        Visibility::Private
65    }
66}
67
68/// Defines visibility levels for a snippet.
69///
70/// Used to control whether a snippet is accessible to the public or not.
71#[derive(Debug, Serialize, Deserialize, Clone)]
72#[serde(rename_all = "lowercase")]
73pub enum Visibility {
74    /// Visible only to the user.
75    Private,
76    /// Publicly visible and accessible.
77    Public,
78    /// Archived, i.e., visible but no longer actively maintained.
79    Archived,
80}
81
82impl From<&str> for Visibility {
83    /// Parses a string into a `Visibility` enum variant.
84    ///
85    /// Unknown values default to `Private`.
86    fn from(s: &str) -> Self {
87        match s {
88            "private" => Visibility::Private,
89            "public" => Visibility::Public,
90            "archived" => Visibility::Archived,
91            _ => Visibility::Private,
92        }
93    }
94}
95/// Represents a complete code snippet, including metadata and the actual content.
96///
97/// Combines [`Meta`] with the snippet's textual content.
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct Snippet {
100    /// Associated metadata (name, language, visibility, etc.).
101    pub meta: Meta,
102    /// Raw code content of the snippet.
103    pub content: String,
104}
105
106impl Snippet {
107    /// Creates a new snippet from the given metadata and content.
108    pub fn new(meta: Meta, content: String) -> Self {
109        Self { meta, content }
110    }
111
112    pub fn to_json(&self) -> String {
113        serde_json::to_string(self)
114            .unwrap_or_else(|_| "{\"type\":\"Other\",\"message\":\"Serialization failed\"}".to_string())
115    }
116
117}
118
119
120#[cfg(feature = "ansi")]
121use colored::*;
122
123impl fmt::Display for Meta {
124    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125        #[cfg(feature = "ansi")]
126        {
127            writeln!(f, "{}: {}", "Name".bold().cyan(), self.name)?;
128            writeln!(f, "{}: {}", "Description".bold().cyan(), self.description)?;
129            writeln!(f, "{}: {}", "Tags".bold().cyan(), self.tags.join(", "))?;
130            writeln!(f, "{}: {}", "Language".bold().cyan(), self.language)?;
131            writeln!(f, "{}: {}", "Created".bold().cyan(), self.created)?;
132            writeln!(f, "{}: {}", "Modified".bold().cyan(), self.modified)?;
133            writeln!(f, "{}: {}", "Visibility".bold().cyan(), format!("{:?}", self.visibility))?;
134            Ok(())
135        }
136
137        #[cfg(not(feature = "ansi"))]
138        {
139            writeln!(f, "Name: {}", self.name)?;
140            writeln!(f, "Description: {}", self.description)?;
141            writeln!(f, "Tags: {}", self.tags.join(", "))?;
142            writeln!(f, "Language: {}", self.language)?;
143            writeln!(f, "Created: {}", self.created)?;
144            writeln!(f, "Modified: {}", self.modified)?;
145            writeln!(f, "Visibility: {:?}", self.visibility)?;
146            Ok(())
147        }
148    }
149}
150
151
152impl fmt::Display for Snippet {
153    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154        #[cfg(feature = "ansi")]
155        {
156            writeln!(f, "{}\n", self.meta)?;
157            writeln!(f, "{}\n{}", "Content:".bold().green(), self.content)
158        }
159
160        #[cfg(not(feature = "ansi"))]
161        {
162            writeln!(f, "{}\n", self.meta)?;
163            writeln!(f, "Content:\n{}", self.content)
164        }
165    }
166}