11use std:: {
22 env, fs,
3- io:: { self , Read } ,
3+ io:: { self , IsTerminal , Read } ,
44 process:: Command ,
55} ;
66
@@ -48,6 +48,8 @@ enum Action {
4848 ext : Option < String > ,
4949 #[ arg( long, help = "Encrypt the snippet (prompted for password)" ) ]
5050 encrypt : bool ,
51+ #[ arg( long, short, num_args = 1 , help = "Add a description to metadata" ) ]
52+ description : Option < String > ,
5153 } ,
5254 #[ command( about = "List all snippets" , alias = "l" ) ]
5355 List {
@@ -63,6 +65,8 @@ enum Action {
6365 name : String ,
6466 #[ arg( short, long, num_args = 1 .., help = "Update tags" ) ]
6567 tags : Option < Vec < String > > ,
68+ #[ arg( long, short, num_args = 1 , help = "Add a description to metadata" ) ]
69+ description : Option < String > ,
6670 } ,
6771 #[ command( about = "Search a query in snippets" , alias = "s" ) ]
6872 Search {
@@ -171,14 +175,15 @@ fn main() -> Result<()> {
171175 tags,
172176 ext,
173177 encrypt,
178+ description,
174179 } => {
175180 if storage. exists ( & name) {
176181 return Err ( anyhow ! ( "snippet '{}' already exists" , name) ) ;
177182 }
178183
179184 let content = if let Some ( path) = file_name {
180185 fs:: read_to_string ( & path) . with_context ( || format ! ( "failed to read '{}'" , path) ) ?
181- } else if atty :: is ( atty :: Stream :: Stdin ) {
186+ } else if std :: io :: stdin ( ) . is_terminal ( ) {
182187 open_editor ( None , ext. as_deref ( ) , encrypt) ?
183188 } else {
184189 let mut buf = String :: new ( ) ;
@@ -193,14 +198,15 @@ fn main() -> Result<()> {
193198 }
194199
195200 let meta = SnippetMeta {
201+ description,
196202 modified_at : now_secs ( ) ,
197203 tags : tags. unwrap_or_default ( ) ,
198204 ext,
199205 } ;
200206
201- if encrypt && !meta. tags . is_empty ( ) {
207+ if encrypt && ( !meta. tags . is_empty ( ) || meta . description . is_some ( ) ) {
202208 eprintln ! (
203- "{} tags are stored in plaintext, avoid sensitive names" ,
209+ "{} tags and description are stored in plaintext, avoid sensitive names" ,
204210 "Warning:" . yellow( ) . bold( )
205211 ) ;
206212 }
@@ -232,13 +238,13 @@ fn main() -> Result<()> {
232238 }
233239
234240 eprintln ! ( "{} {} snippets\n " , "sinbo" . cyan( ) . bold( ) , snippets. len( ) ) ;
235-
236241 for s in & snippets {
237- let tags_str = if s. meta . tags . is_empty ( ) {
238- String :: new ( )
239- } else {
240- format ! ( " [{}]" , s. meta. tags. join( ", " ) . dimmed( ) )
241- } ;
242+ let ext_raw = s
243+ . meta
244+ . ext
245+ . as_deref ( )
246+ . map ( |e| format ! ( " .{e}" ) )
247+ . unwrap_or_default ( ) ;
242248
243249 let ext_str = s
244250 . meta
@@ -247,13 +253,44 @@ fn main() -> Result<()> {
247253 . map ( |e| format ! ( " .{}" , e. bright_black( ) ) )
248254 . unwrap_or_default ( ) ;
249255
250- let enc_str = if s. encrypted {
251- format ! ( " {}" , "Locked" . yellow( ) . dimmed( ) )
256+ let desc_str = s. meta . description . clone ( ) . unwrap_or_default ( ) ;
257+
258+ let tags_raw = if s. meta . tags . is_empty ( ) {
259+ String :: new ( )
252260 } else {
261+ format ! ( "[{}]" , s. meta. tags. join( ", " ) )
262+ } ;
263+
264+ let tags_str = if s. meta . tags . is_empty ( ) {
253265 String :: new ( )
266+ } else {
267+ format ! ( "[{}]" , s. meta. tags. join( ", " ) . dimmed( ) )
268+ } ;
269+
270+ let name_ext_raw = format ! ( "{}{}" , s. name, ext_raw) ;
271+ let pad = 20usize . saturating_sub ( name_ext_raw. len ( ) ) ;
272+
273+ let tags_pad = 30usize . saturating_sub ( tags_raw. len ( ) ) ;
274+
275+ let end = if s. encrypted {
276+ format ! (
277+ "{} --- {}" ,
278+ "Locked" . yellow( ) . dimmed( ) ,
279+ desc_str. bright_black( ) . italic( )
280+ )
281+ } else {
282+ desc_str. bright_black ( ) . italic ( ) . to_string ( )
254283 } ;
255284
256- println ! ( "{}{}{}{}" , s. name. cyan( ) . bold( ) , tags_str, ext_str, enc_str) ;
285+ println ! (
286+ "{}{}{}{}{}{}" ,
287+ s. name. cyan( ) . bold( ) ,
288+ ext_str,
289+ " " . repeat( pad) ,
290+ tags_str,
291+ " " . repeat( tags_pad) ,
292+ end
293+ ) ;
257294
258295 if show {
259296 if s. encrypted {
@@ -270,7 +307,11 @@ fn main() -> Result<()> {
270307 storage. remove ( & name) ?;
271308 eprintln ! ( "{} removed '{}'" , "sinbo" . cyan( ) . bold( ) , name. yellow( ) ) ;
272309 }
273- Action :: Edit { name, tags } => {
310+ Action :: Edit {
311+ name,
312+ tags,
313+ description,
314+ } => {
274315 let snippet = storage
275316 . get ( & name)
276317 . with_context ( || format ! ( "snippet '{}' not found" , name) ) ?;
@@ -282,7 +323,7 @@ fn main() -> Result<()> {
282323 ) ) ;
283324 }
284325
285- let content = if atty :: is ( atty :: Stream :: Stdin ) {
326+ let content = if std :: io :: stdin ( ) . is_terminal ( ) {
286327 open_editor ( Some ( & snippet. content ) , snippet. meta . ext . as_deref ( ) , false ) ?
287328 } else {
288329 let mut buf = String :: new ( ) ;
@@ -297,6 +338,7 @@ fn main() -> Result<()> {
297338 }
298339
299340 let meta = SnippetMeta {
341+ description,
300342 modified_at : now_secs ( ) ,
301343 tags : tags. unwrap_or ( snippet. meta . tags ) ,
302344 ext : snippet. meta . ext ,
0 commit comments