1- use std:: sync:: LazyLock ;
1+ use std:: {
2+ cell:: RefCell ,
3+ path:: { Path , PathBuf } ,
4+ sync:: LazyLock ,
5+ } ;
26
3- #[ derive( Clone , Debug ) ]
4- pub struct SupportedTerminal {
7+ use gtk:: glib;
8+ use tracing:: { error, info} ;
9+
10+ use crate :: {
11+ config,
12+ distrobox:: { Command , CommandRunner , RealCommandRunner } ,
13+ } ;
14+
15+ use gtk:: subclass:: prelude:: * ;
16+
17+ #[ derive( Clone , Debug , serde:: Serialize , serde:: Deserialize ) ]
18+ pub struct Terminal {
519 pub name : String ,
620 pub program : String ,
721 pub separator_arg : String ,
22+ pub read_only : bool ,
823}
924
10- pub static SUPPORTED_TERMINALS : LazyLock < Vec < SupportedTerminal > > = LazyLock :: new ( || {
25+ static SUPPORTED_TERMINALS : LazyLock < Vec < Terminal > > = LazyLock :: new ( || {
1126 [
1227 ( "GNOME Console" , "kgx" , "--" ) ,
1328 ( "GNOME Terminal" , "gnome-terminal" , "--" ) ,
@@ -26,14 +41,148 @@ pub static SUPPORTED_TERMINALS: LazyLock<Vec<SupportedTerminal>> = LazyLock::new
2641 ( "Terminator" , "terminator" , "-x" ) ,
2742 ]
2843 . iter ( )
29- . map ( |( name, program, separator_arg) | SupportedTerminal {
44+ . map ( |( name, program, separator_arg) | Terminal {
3045 name : name. to_string ( ) ,
3146 program : program. to_string ( ) ,
3247 separator_arg : separator_arg. to_string ( ) ,
48+ read_only : true ,
3349 } )
3450 . collect ( )
3551} ) ;
3652
37- pub fn terminal_by_name ( name : & str ) -> Option < SupportedTerminal > {
38- SUPPORTED_TERMINALS . iter ( ) . find ( |x| x. name == name) . cloned ( )
53+ mod imp {
54+ use super :: * ;
55+ use std:: cell:: RefCell ;
56+
57+ pub struct TerminalRepository {
58+ pub list : RefCell < Vec < Terminal > > ,
59+ pub custom_list_path : PathBuf ,
60+ pub command_runner : RefCell < Box < dyn CommandRunner > > ,
61+ }
62+
63+ impl Default for TerminalRepository {
64+ fn default ( ) -> Self {
65+ let custom_list_path = glib:: user_data_dir ( ) . join ( "distroshelf-terminals.json" ) ;
66+ Self {
67+ list : RefCell :: new ( vec ! [ ] ) ,
68+ custom_list_path,
69+ command_runner : RefCell :: new ( Box :: new ( RealCommandRunner { } ) ) ,
70+ }
71+ }
72+ }
73+ impl ObjectImpl for TerminalRepository { }
74+
75+ #[ glib:: object_subclass]
76+ impl ObjectSubclass for TerminalRepository {
77+ const NAME : & ' static str = "TerminalRepository" ;
78+ type Type = super :: TerminalRepository ;
79+ }
80+ }
81+
82+
83+ glib:: wrapper! {
84+ pub struct TerminalRepository ( ObjectSubclass <imp:: TerminalRepository >) ;
85+ }
86+
87+ impl TerminalRepository {
88+ pub fn new ( ) -> Self {
89+ let this: Self = glib:: Object :: builder ( )
90+ . build ( ) ;
91+
92+ let mut list = SUPPORTED_TERMINALS . clone ( ) ;
93+ if let Ok ( loaded_list) = Self :: load_terminals_from_json ( & this. imp ( ) . custom_list_path ) {
94+ list. extend ( loaded_list) ;
95+ } else {
96+ error ! ( "Failed to load custom terminals from JSON file {:?}" , & this. imp( ) . custom_list_path) ;
97+ }
98+
99+ list. sort_by ( |a, b| a. name . cmp ( & b. name ) ) ;
100+ this. imp ( ) . list . replace ( list) ;
101+ this
102+ }
103+
104+ pub fn is_read_only ( & self , name : & str ) -> bool {
105+ self . imp ( ) . list
106+ . borrow ( )
107+ . iter ( )
108+ . find ( |x| x. name == name)
109+ . map_or ( false , |x| x. read_only )
110+ }
111+
112+ pub fn save_terminal ( & self , terminal : Terminal ) -> anyhow:: Result < ( ) > {
113+ if self . is_read_only ( terminal. name . as_str ( ) ) {
114+ return Err ( anyhow:: anyhow!( "Cannot modify read-only terminal" ) ) ;
115+ }
116+ let mut list = self . imp ( ) . list . borrow_mut ( ) ;
117+ list. retain ( |x| x. name != terminal. name ) ;
118+ list. push ( terminal) ;
119+
120+ list. sort_by ( |a, b| a. name . cmp ( & b. name ) ) ;
121+
122+ self . save_terminals_to_json ( ) ;
123+ Ok ( ( ) )
124+ }
125+
126+ pub fn delete_terminal ( & self , name : & str ) -> anyhow:: Result < ( ) > {
127+ if self . is_read_only ( name) {
128+ return Err ( anyhow:: anyhow!( "Cannot modify read-only terminal" ) ) ;
129+ }
130+ self . imp ( ) . list . borrow_mut ( ) . retain ( |x| x. name != name) ;
131+ Ok ( ( ) )
132+ }
133+
134+ pub fn terminal_by_name ( & self , name : & str ) -> Option < Terminal > {
135+ self . imp ( ) . list . borrow ( ) . iter ( ) . find ( |x| x. name == name) . cloned ( )
136+ }
137+
138+ pub fn terminal_by_program ( & self , program : & str ) -> Option < Terminal > {
139+ self . imp ( ) . list
140+ . borrow ( )
141+ . iter ( )
142+ . find ( |x| x. program == program)
143+ . cloned ( )
144+ }
145+
146+ pub fn all_terminals ( & self ) -> Vec < Terminal > {
147+ self . imp ( ) . list . borrow ( ) . clone ( )
148+ }
149+
150+ fn save_terminals_to_json ( & self ) {
151+ let list: Vec < Terminal > = self . imp ( ) . list . borrow ( ) . iter ( ) . filter ( |x| !x. read_only ) . cloned ( ) . collect :: < Vec < _ > > ( ) ;
152+ let json = serde_json:: to_string ( & * list) . unwrap ( ) ;
153+ std:: fs:: write ( & self . imp ( ) . custom_list_path , json) . unwrap ( ) ;
154+ }
155+
156+ fn load_terminals_from_json ( path : & Path ) -> anyhow:: Result < Vec < Terminal > > {
157+ let data = std:: fs:: read_to_string ( path) ?;
158+ let list: Vec < Terminal > = serde_json:: from_str ( & data) ?;
159+ Ok ( list)
160+ }
161+
162+ pub async fn default_terminal ( & self ) -> Option < Terminal > {
163+ let command = Command :: new_with_args ( "flatpak-spawn" , & [ "--host" , "--" , "get" ,
164+ "org.gnome.desktop.default-applications.terminal" ,
165+ "exec" , ] ) ;
166+ let output = self . imp ( ) . command_runner . borrow ( ) . output ( command. clone ( ) ) ;
167+ let Ok ( output) = output. await else {
168+ error ! ( "Failed to get default terminal, running {:?}" , & command) ;
169+ return None ;
170+ } ;
171+ let terminal_program = String :: from_utf8 ( output. stdout ) . unwrap ( ) . trim ( ) . to_string ( ) ;
172+ if terminal_program. is_empty ( ) {
173+ return None ;
174+ }
175+ info ! ( "Default terminal program: {}" , terminal_program) ;
176+ self . terminal_by_program ( & terminal_program)
177+ . or_else ( || {
178+ error ! ( "Terminal program {} not found in the list" , terminal_program) ;
179+ None
180+ } )
181+ }
182+ }
183+
184+ impl Default for TerminalRepository {
185+ fn default ( ) -> Self {
186+ Self :: new ( )
187+ }
39188}
0 commit comments