mafa/
lib.rs

1// Copyright (C) 2023 Michael Lee <micl2e2@proton.me>
2//
3// Licensed under the GNU General Public License, Version 3.0 or any later
4// version <LICENSE-GPL or https://www.gnu.org/licenses/gpl-3.0.txt>.
5//
6// This file may not be copied, modified, or distributed except in compliance
7// with the license.
8//
9
10use std::borrow::Cow;
11use std::sync::Arc;
12use std::sync::Mutex;
13use wda::{GeckoDriver, WdaError, WdaSett, WdcError, WebDrvAstn};
14
15use clap::Arg as ClapArg;
16use clap::ArgAction as ClapArgAction;
17use clap::ArgMatches as ClapArgMatches;
18use clap::Command as ClapCommand;
19
20pub mod error;
21
22pub use error::MafaError;
23use error::Result;
24
25#[macro_use]
26mod private_macros;
27
28pub mod mafadata;
29use mafadata::MafaData;
30
31pub mod ev_ntf;
32use ev_ntf::EurKind;
33use ev_ntf::EventNotifier;
34
35#[cfg(any(feature = "twtl", feature = "gtrans", feature = "camd"))]
36mod comm;
37#[cfg(any(feature = "twtl", feature = "gtrans", feature = "camd"))]
38use comm::CacheMechanism;
39
40#[cfg(feature = "twtl")]
41pub mod twtl;
42
43#[cfg(feature = "gtrans")]
44pub mod gtrans;
45
46#[cfg(feature = "camd")]
47pub mod camd;
48
49#[derive(Debug, Default)]
50pub struct MafaInput {
51    pub silent: bool,
52    pub nocolor: bool,
53    pub ascii: bool,
54    pub wrap_width: u16,
55    pub wrap_may_break: bool,
56    pub tout_page_load: u32,
57    pub tout_script: u32,
58    pub socks5: String,
59    pub gui: bool,
60    pub list_profile: bool,
61    pub use_profile: String,
62    cachm: CacheMechanism,
63    pub elap: bool,
64}
65
66impl MafaInput {
67    pub fn from_ca_matched(ca_matched: &ClapArgMatches) -> Result<Self> {
68        let mut mafa_in = MafaInput::default();
69
70        // silent
71        if ca_matched.get_flag(opts::SilentMode::id()) {
72            mafa_in.silent = true;
73        }
74
75        // nocolor
76        if ca_matched.get_flag(opts::NoColorMode::id()) {
77            mafa_in.nocolor = true;
78        }
79
80        // ascii
81        if ca_matched.get_flag(opts::AsciiMode::id()) {
82            mafa_in.ascii = true;
83        }
84
85        // wrap-width
86        if let Ok(Some(optval)) = ca_matched.try_get_one::<String>(opts::WrapWidth::id()) {
87            let intval =
88                u16::from_str_radix(&optval, 10).map_err(|_| MafaError::InvalidWrapWidth)?;
89            mafa_in.wrap_width = intval;
90        }
91
92        // wrap-may-break
93        if ca_matched.get_flag(opts::WrapMayBreak::id()) {
94            mafa_in.wrap_may_break = true;
95        }
96
97        // page load timeout
98        if let Ok(Some(optval)) = ca_matched.try_get_one::<String>(opts::TimeoutPageLoad::id()) {
99            mafa_in.tout_page_load = u32::from_str_radix(&optval, 10)
100                .or_else(|_| Err(MafaError::InvalidTimeoutPageLoad))?;
101        } else {
102            mafa_in.tout_page_load = u32::from_str_radix(opts::TimeoutPageLoad::def_val(), 10)
103                .or_else(|_| Err(MafaError::Buggy))?;
104        }
105
106        // script timeout
107        if let Ok(Some(optval)) = ca_matched.try_get_one::<String>(opts::TimeoutScript::id()) {
108            mafa_in.tout_script = u32::from_str_radix(&optval, 10)
109                .or_else(|_| Err(MafaError::InvalidTimeoutScript))?;
110        } else {
111            mafa_in.tout_script = u32::from_str_radix(opts::TimeoutScript::def_val(), 10)
112                .or_else(|_| Err(MafaError::Buggy))?;
113        }
114
115        // socks5
116        if let Ok(Some(val)) = ca_matched.try_get_one::<String>(opts::Socks5Proxy::id()) {
117            mafa_in.socks5 = val.clone();
118        }
119
120        // gui
121        if ca_matched.get_flag(opts::GuiMode::id()) {
122            mafa_in.gui = true;
123        }
124
125        // elap
126        if ca_matched.get_flag(opts::Elapsed::id()) {
127            mafa_in.elap = true;
128        }
129
130        // cachm
131        if let Ok(Some(optval)) = ca_matched.try_get_one::<String>(opts::CacheMech::id()) {
132            mafa_in.cachm = CacheMechanism::from_str(optval);
133        }
134
135        // list profile
136        if ca_matched.get_flag(opts::ListProfile::id()) {
137            mafa_in.list_profile = true;
138        }
139
140        // use profile
141        if let Ok(Some(val)) = ca_matched.try_get_one::<String>(opts::UseProfile::id()) {
142            mafa_in.use_profile = val.clone();
143        }
144
145        dbgg!(&mafa_in);
146
147        Ok(mafa_in)
148    }
149}
150
151// opts //
152
153pub mod opts {
154    use core::ops::Range;
155    #[allow(unused)]
156    use core::ops::RangeFrom;
157
158    pub struct SilentMode;
159    impl SilentMode {
160        #[inline]
161        pub fn id() -> &'static str {
162            "SILENT_MODE"
163        }
164        #[inline]
165        pub fn longopt() -> &'static str {
166            "silent"
167        }
168        #[inline]
169        pub fn helper() -> &'static str {
170            "Enable silent mode"
171        }
172        #[inline]
173        pub fn long_helper() -> String {
174            let bf = "Enable silent mode
175
176Any insignificant output redirected to standard output will be hidden.";
177
178            let mut af_buf = [0u8; 256];
179
180            let rl = bwrap::Wrapper::new(bf, 70, &mut af_buf)
181                .unwrap()
182                .wrap()
183                .unwrap();
184
185            String::from_utf8_lossy(&af_buf[0..rl]).to_string()
186        }
187    }
188
189    pub struct NoColorMode;
190    impl NoColorMode {
191        #[inline]
192        pub fn id() -> &'static str {
193            "NOCOLOR_MODE"
194        }
195        #[inline]
196        pub fn longopt() -> &'static str {
197            "nocolor"
198        }
199        #[inline]
200        pub fn helper() -> &'static str {
201            "Enable non-color mode"
202        }
203        #[inline]
204        pub fn long_helper() -> String {
205            let bf = "Enable non-color mode
206
207Any output will be printed without color. Default is with color.";
208
209            let mut af_buf = [0u8; 256];
210
211            let rl = bwrap::Wrapper::new(bf, 70, &mut af_buf)
212                .unwrap()
213                .wrap()
214                .unwrap();
215
216            String::from_utf8_lossy(&af_buf[0..rl]).to_string()
217        }
218    }
219
220    pub struct AsciiMode;
221    impl AsciiMode {
222        #[inline]
223        pub fn id() -> &'static str {
224            "ASCIIMODE"
225        }
226        #[inline]
227        pub fn longopt() -> &'static str {
228            "ascii"
229        }
230        #[inline]
231        pub fn helper() -> &'static str {
232            "Use classical ASCII style"
233        }
234        #[inline]
235        pub fn long_helper() -> String {
236            let bf = r#"Use classical ASCII style
237
238ASCII style only allows ASCII characters to mafa's meta information displaying. Default is Unicode mode, which allows more distinguishable characters for displaying.
239
240NOTE: the minimum is 18, any value smaller than 18 will fallback to 80."#;
241            let mut af_buf = [0u8; 128];
242
243            let rl = bwrap::Wrapper::new(bf, 70, &mut af_buf)
244                .unwrap()
245                .wrap()
246                .unwrap();
247
248            String::from_utf8_lossy(&af_buf[0..rl]).to_string()
249        }
250    }
251
252    pub struct WrapWidth;
253    impl WrapWidth {
254        #[inline]
255        pub fn id() -> &'static str {
256            "WRAPWIDTH"
257        }
258        #[inline]
259        pub fn n_args() -> Range<usize> {
260            1..2
261        }
262        #[inline]
263        pub fn longopt() -> &'static str {
264            "wrap-width"
265        }
266        #[inline]
267        pub fn def_val() -> &'static str {
268            "80"
269        }
270        #[inline]
271        pub fn helper() -> &'static str {
272            "Wrap output with a width limit"
273        }
274        #[inline]
275        pub fn long_helper() -> String {
276            let bf = r#"Wrap output with a width limit
277
278NOTE: the minimun is 18, any value smaller than 18 will fallback to 80."#;
279            let mut af_buf = [0u8; 128];
280
281            let rl = bwrap::Wrapper::new(bf, 70, &mut af_buf)
282                .unwrap()
283                .wrap()
284                .unwrap();
285
286            String::from_utf8_lossy(&af_buf[0..rl]).to_string()
287        }
288    }
289
290    pub struct WrapMayBreak;
291    impl WrapMayBreak {
292        #[inline]
293        pub fn id() -> &'static str {
294            "WRAPMAYBREAK"
295        }
296        #[inline]
297        pub fn longopt() -> &'static str {
298            "wrap-may-break"
299        }
300        #[inline]
301        pub fn helper() -> &'static str {
302            "Wrap output in MayBreak style"
303        }
304        #[inline]
305        pub fn long_helper() -> String {
306            let bf = r#"Wrap output in MayBreak style
307
308Default is "NoBreak" style. This change the style to "MayBreak".
309
310NOTE: This is a hint option, components may ignore it.
311
312NOTE: "NoBreak" suits for languages that rely om ASCII SPACE to delimit words. "MayBreak" suits for otherwise languages. See more details in Bwrap documentation: https://docs.rs/bwrap/latest/bwrap/enum.WrapStyle.html."#;
313            let mut af_buf = [0u8; 512];
314
315            let rl = bwrap::Wrapper::new(bf, 70, &mut af_buf)
316                .unwrap()
317                .wrap()
318                .unwrap();
319
320            String::from_utf8_lossy(&af_buf[0..rl]).to_string()
321        }
322    }
323
324    pub struct GuiMode;
325    impl GuiMode {
326        #[inline]
327        pub fn id() -> &'static str {
328            "GUI_MODE"
329        }
330        #[inline]
331        pub fn longopt() -> &'static str {
332            "gui"
333        }
334        #[inline]
335        pub fn helper() -> &'static str {
336            "Enable GUI mode"
337        }
338        #[inline]
339        pub fn long_helper() -> String {
340            let bf = "Enable GUI mode
341
342Run the underlying web browser in GUI mode.
343
344NOTE: when GUI mode is on, any user operation on web browser interface MAY affect mafa's correctness, use with caution.";
345
346            let mut af_buf = [0u8; 256];
347
348            let rl = bwrap::Wrapper::new(bf, 70, &mut af_buf)
349                .unwrap()
350                .wrap()
351                .unwrap();
352
353            String::from_utf8_lossy(&af_buf[0..rl]).to_string()
354        }
355    }
356
357    pub struct ListProfile;
358    impl ListProfile {
359        #[inline]
360        pub fn id() -> &'static str {
361            "LIST_PROFILE"
362        }
363        #[inline]
364        pub fn longopt() -> &'static str {
365            "list-p"
366        }
367        #[inline]
368        pub fn helper() -> &'static str {
369            "List all existing browser profiles"
370        }
371        #[inline]
372        pub fn long_helper() -> String {
373            let bf = "List all existing profiles's ID
374
375Profiles' ID is surrounded by a pair of brackets:
376...
377<PROFILE ID>
378<PROFILE ID>
379<PROFILE ID>
380...";
381
382            let mut af_buf = [0u8; 256];
383
384            let rl = bwrap::Wrapper::new(bf, 70, &mut af_buf)
385                .unwrap()
386                .wrap()
387                .unwrap();
388
389            String::from_utf8_lossy(&af_buf[0..rl]).to_string()
390        }
391    }
392
393    pub struct UseProfile;
394    impl UseProfile {
395        #[inline]
396        pub fn id() -> &'static str {
397            "USE_PROFILE"
398        }
399        #[inline]
400        pub fn n_args() -> Range<usize> {
401            1..2
402        }
403        #[inline]
404        pub fn longopt() -> &'static str {
405            "profile"
406        }
407        #[inline]
408        pub fn shortopt() -> char {
409            'p'
410        }
411        #[inline]
412        pub fn helper() -> &'static str {
413            "Use specific browser profile"
414        }
415        #[inline]
416        pub fn long_helper() -> String {
417            let bf = "Use specific profile ID
418
419Profile IDs are strings including ONLY letters, nimbers or hyphens.
420
421NOTE: the profile will be created if not existing. Browser profiles' size is non-trivial, use with caution.
422";
423
424            let mut af_buf = [0u8; 256];
425
426            let rl = bwrap::Wrapper::new(bf, 70, &mut af_buf)
427                .unwrap()
428                .wrap()
429                .unwrap();
430
431            String::from_utf8_lossy(&af_buf[0..rl]).to_string()
432        }
433    }
434
435    pub struct Socks5Proxy;
436    impl Socks5Proxy {
437        #[inline]
438        pub fn id() -> &'static str {
439            "SOCKS5_PROXY"
440        }
441        #[inline]
442        pub fn n_args() -> Range<usize> {
443            1..2
444        }
445        #[inline]
446        pub fn longopt() -> &'static str {
447            "socks5"
448        }
449        #[inline]
450        pub fn helper() -> &'static str {
451            "Fetch data through SOCKS5 praxy                                        "
452        }
453        #[inline]
454        pub fn long_helper() -> String {
455            let bf = "Fetch data through SOCKS5 proxy
456
457The default SOCKS5 proxy used by the underlying web browser to fetch data. This is also used by mafa to fetch webdriver server binaries when initialization.";
458
459            let mut af_buf = [0u8; 256];
460
461            let rl = bwrap::Wrapper::new(bf, 70, &mut af_buf)
462                .unwrap()
463                .wrap()
464                .unwrap();
465
466            String::from_utf8_lossy(&af_buf[0..rl]).to_string()
467        }
468    }
469
470    pub struct TimeoutPageLoad;
471    impl TimeoutPageLoad {
472        #[inline]
473        pub fn id() -> &'static str {
474            "TIMEOUT_PAGE_LOAD"
475        }
476        #[inline]
477        pub fn longopt() -> &'static str {
478            "timeout-pageload"
479        }
480        #[inline]
481        pub fn n_args() -> Range<usize> {
482            1..2
483        }
484        #[inline]
485        pub fn def_val() -> &'static str {
486            "30000"
487        }
488        #[inline]
489        pub fn helper() -> &'static str {
490            "Timeout for page loading(ms)"
491        }
492        #[inline]
493        pub fn long_helper() -> String {
494            let bf = "Timeout for page loading(ms)
495
496The default timeout for loading web page(i.e., opening a website). Refer to WebDriver standard(https://www.w3.org/TR/webdriver2/#timeouts) for more details.
497
498NOTE: customizing this option mostly brings the negative effect for page loading, do not use unless you know what you are doing.";
499            let mut af_buf = [0u8; 512];
500
501            let rl = bwrap::Wrapper::new(bf, 70, &mut af_buf)
502                .unwrap()
503                .wrap()
504                .unwrap();
505
506            String::from_utf8_lossy(&af_buf[0..rl]).to_string()
507        }
508    }
509
510    pub struct TimeoutScript;
511    impl TimeoutScript {
512        #[inline]
513        pub fn id() -> &'static str {
514            "TIMEOUT_SCRIPT"
515        }
516        #[inline]
517        pub fn longopt() -> &'static str {
518            "timeout-script"
519        }
520        #[inline]
521        pub fn n_args() -> Range<usize> {
522            1..2
523        }
524        #[inline]
525        pub fn def_val() -> &'static str {
526            "30000"
527        }
528        #[inline]
529        pub fn helper() -> &'static str {
530            "Timeout for script evaluation(ms)"
531        }
532        #[inline]
533        pub fn long_helper() -> String {
534            let bf = "Timeout for script evaluation(ms)
535
536The default timeout for script evaluation(i.e., evaluating JavaScript synchronously or asynchronously). Refer to WebDriver standard(https://www.w3.org/TR/webdriver2/#timeouts) for more details.
537
538NOTE: customizing this option mostly brings the negative effect for script evaluation, do not use unless you know what you are doing.";
539            let mut af_buf = [0u8; 512];
540
541            let rl = bwrap::Wrapper::new(bf, 70, &mut af_buf)
542                .unwrap()
543                .wrap()
544                .unwrap();
545
546            String::from_utf8_lossy(&af_buf[0..rl]).to_string()
547        }
548    }
549
550    pub struct CacheMech;
551    impl CacheMech {
552        #[inline]
553        pub fn id() -> &'static str {
554            "CACHE_MECHNISM"
555        }
556        #[inline]
557        pub fn n_args() -> Range<usize> {
558            1..2
559        }
560        #[inline]
561        pub fn longopt() -> &'static str {
562            "cache"
563        }
564        #[inline]
565        pub fn def_val() -> &'static str {
566            "LOCAL"
567        }
568        #[inline]
569        pub fn helper() -> &'static str {
570            "The caching mechanism"
571        }
572        #[inline]
573        pub fn long_helper() -> String {
574            let bf = r#"The caching mechanism
575
576Avvilable values are: LOCAL, REMOTE, NO.
577
578LOCAL instructs mafa to use local cache, typically located in mafa's dedicated cache directory; REMOTE instructs mafa to use remote cache, which is stored in a dedicated remote repository, note that this option overrides all existing caches; NO instructs mafa to build cache freshly, this may need more time, compared to other mechanisms.
579
580Performance : LOCAL > REMOTE > NO
581Stability   :    NO > REMOTE > LOCAL"#;
582            let mut af_buf = [0u8; 512];
583
584            let rl = bwrap::Wrapper::new(bf, 70, &mut af_buf)
585                .unwrap()
586                .wrap()
587                .unwrap();
588
589            String::from_utf8_lossy(&af_buf[0..rl]).to_string()
590        }
591    }
592
593    pub struct Elapsed;
594    impl Elapsed {
595        #[inline]
596        pub fn id() -> &'static str {
597            "ELAPSED"
598        }
599        #[inline]
600        pub fn longopt() -> &'static str {
601            "elap"
602        }
603        #[inline]
604        pub fn helper() -> &'static str {
605            "Report the time cost in major phases"
606        }
607    }
608}
609
610pub fn get_cmd() -> ClapCommand {
611    let opt_silient = {
612        type O = opts::SilentMode;
613        ClapArg::new(O::id())
614            .long(O::longopt())
615            .action(ClapArgAction::SetTrue)
616            .help(O::helper())
617            .long_help(O::long_helper())
618    };
619
620    let opt_nocolor = {
621        type O = opts::NoColorMode;
622        ClapArg::new(O::id())
623            .long(O::longopt())
624            .action(ClapArgAction::SetTrue)
625            .help(O::helper())
626            .long_help(O::long_helper())
627    };
628
629    let opt_ascii = {
630        type O = opts::AsciiMode;
631        ClapArg::new(O::id())
632            .long(O::longopt())
633            .action(ClapArgAction::SetTrue)
634            .help(O::helper())
635    };
636
637    let opt_wrapwidth = {
638        type O = opts::WrapWidth;
639        ClapArg::new(O::id())
640            .long(O::longopt())
641            .default_value(O::def_val())
642            .help(O::helper())
643            .long_help(O::long_helper())
644    };
645
646    let opt_wrapmaybreak = {
647        type O = opts::WrapMayBreak;
648        ClapArg::new(O::id())
649            .long(O::longopt())
650            .action(ClapArgAction::SetTrue)
651            .help(O::helper())
652            .long_help(O::long_helper())
653    };
654
655    let opt_gui = {
656        type O = opts::GuiMode;
657        ClapArg::new(O::id())
658            .long(O::longopt())
659            .action(ClapArgAction::SetTrue)
660            .help(O::helper())
661            .long_help(O::long_helper())
662    };
663
664    let opt_socks5 = {
665        type O = opts::Socks5Proxy;
666        ClapArg::new(O::id())
667            .long(O::longopt())
668            .num_args(O::n_args())
669            .help(O::helper())
670            .long_help(O::long_helper())
671    };
672
673    let opt_tout_pageload = {
674        type O = opts::TimeoutPageLoad;
675        ClapArg::new(O::id())
676            .long(O::longopt())
677            .num_args(O::n_args())
678            .default_value(O::def_val())
679            .help(O::helper())
680            .long_help(O::long_helper())
681    };
682
683    let opt_tout_script = {
684        type O = opts::TimeoutScript;
685        ClapArg::new(O::id())
686            .long(O::longopt())
687            .num_args(O::n_args())
688            .default_value(O::def_val())
689            .help(O::helper())
690            .long_help(O::long_helper())
691    };
692
693    let opt_cachm = {
694        type O = opts::CacheMech;
695        ClapArg::new(O::id())
696            .long(O::longopt())
697            .default_value(O::def_val())
698            .help(O::helper())
699            .long_help(O::long_helper())
700    };
701
702    let opt_elapsed = {
703        type O = opts::Elapsed;
704        ClapArg::new(O::id())
705            .long(O::longopt())
706            .action(ClapArgAction::SetTrue)
707            .help(O::helper())
708    };
709
710    let opt_list_profile = {
711        type O = opts::ListProfile;
712        ClapArg::new(O::id())
713            .long(O::longopt())
714            .action(ClapArgAction::SetTrue)
715            .help(O::helper())
716            .long_help(O::long_helper())
717    };
718
719    let opt_use_profile = {
720        type O = opts::UseProfile;
721        ClapArg::new(O::id())
722            .long(O::longopt())
723            .short(O::shortopt())
724            .num_args(O::n_args())
725            .help(O::helper())
726            .long_help(O::long_helper())
727    };
728
729    static HELPER_TXT: once_cell::sync::Lazy<String> = once_cell::sync::Lazy::new(|| {
730        let mut s = format!("{}\ncomponents: ", clap::crate_version!());
731        #[cfg(feature = "imode")]
732        {
733            s += "IMODE ";
734        }
735        #[cfg(feature = "gtrans")]
736        {
737            s += "GTRANS ";
738        }
739        #[cfg(feature = "twtl")]
740        {
741            s += "TWTL ";
742        }
743        #[cfg(feature = "camd")]
744        {
745            s += "CAMD ";
746        }
747
748        s += "\n\n";
749        s += &format!("Copyright (C) 2023 {}.", clap::crate_authors!());
750        s += "
751This is free software.  You may redistribute copies of it under the terms of
752the GNU General Public License <https://www.gnu.org/licenses/gpl.html>.
753There is NO WARRANTY, to the extent permitted by law.";
754
755        s
756    });
757
758    let cmd_mafa = ClapCommand::new("mafa")
759        .version(clap::crate_version!())
760        .long_version(HELPER_TXT.as_str())
761        .about(clap::crate_description!());
762
763    #[cfg(feature = "imode")]
764    let cmd_mafa = cmd_mafa.subcommand(
765        ClapCommand::new("i")
766            .about("Enter interactive mode")
767            .long_about(
768                "Enter interactive mode
769
770With interactive mode, mafa's components interact with websites statefully,
771performing tasks without full initializtion of the underlying WebDriver,
772this usually results in faster performance.
773
774Note that under interactive mode, components' options are identical to
775ones under normal mode, i.e., -h for short help, --help for long help.
776",
777            ),
778    );
779
780    #[cfg(feature = "twtl")]
781    let cmd_mafa = cmd_mafa.subcommand(twtl::get_cmd());
782
783    #[cfg(feature = "gtrans")]
784    let cmd_mafa = cmd_mafa.subcommand(gtrans::get_cmd());
785
786    #[cfg(feature = "camd")]
787    let cmd_mafa = cmd_mafa.subcommand(camd::get_cmd());
788
789    let cmd_mafa = cmd_mafa
790        .arg(opt_silient)
791        .arg(opt_nocolor)
792        .arg(opt_ascii)
793        .arg(opt_wrapwidth)
794        .arg(opt_wrapmaybreak)
795        .arg(opt_gui)
796        .arg(opt_socks5)
797        .arg(opt_tout_pageload)
798        .arg(opt_tout_script)
799        .arg(opt_cachm)
800        .arg(opt_elapsed)
801        .arg(opt_list_profile)
802        .arg(opt_use_profile);
803
804    cmd_mafa
805}
806
807//
808
809#[derive(Debug)]
810pub struct MafaClient<'a, 'b, 'c, I, C> {
811    mafad: &'a MafaData,
812    ntf: Arc<Mutex<EventNotifier>>,
813    input: &'b MafaInput,
814    sub_input: I,
815    wda: &'c WebDrvAstn<GeckoDriver>,
816    caches: Vec<C>,
817}
818
819impl<'a, 'b, 'c, I, C: Default> MafaClient<'a, 'b, 'c, I, C> {
820    pub fn new(
821        mafad: &'a MafaData,
822        ntf: Arc<Mutex<EventNotifier>>,
823        mafa_in: &'b MafaInput,
824        sub_in: I,
825        wda_inst: &'c WebDrvAstn<GeckoDriver>,
826    ) -> Self {
827        MafaClient {
828            mafad,
829            ntf,
830            input: mafa_in,
831            sub_input: sub_in,
832            wda: wda_inst,
833            caches: Default::default(),
834        }
835    }
836
837    pub fn set_sub_input(&mut self, newin: I) {
838        self.sub_input = newin;
839    }
840}
841
842fn get_wda_setts(mafa_in: &MafaInput) -> Vec<WdaSett> {
843    let mut wda_setts = vec![];
844
845    // gui
846    if !mafa_in.gui {
847        wda_setts.push(WdaSett::NoGui);
848    }
849
850    // socks5
851    if comm::is_valid_socks5(&mafa_in.socks5) {
852        wda_setts.push(WdaSett::PrepareUseSocksProxy(Cow::Borrowed(
853            &mafa_in.socks5,
854        )));
855        wda_setts.push(WdaSett::Socks5Proxy(Cow::Borrowed(&mafa_in.socks5)));
856        wda_setts.push(WdaSett::ProxyDnsSocks5);
857    }
858
859    // tout pageload
860    wda_setts.push(WdaSett::PageLoadTimeout(mafa_in.tout_page_load));
861
862    // tout script
863    wda_setts.push(WdaSett::ScriptTimeout(mafa_in.tout_script));
864
865    // profile
866    if mafa_in.use_profile.len() > 0 {
867        wda_setts.push(WdaSett::BrowserProfileId(Cow::Borrowed(
868            &mafa_in.use_profile,
869        )))
870    }
871
872    dbgg!(&wda_setts);
873
874    wda_setts
875}
876
877pub fn init_wda(mafa_in: &MafaInput) -> Result<WebDrvAstn<GeckoDriver>> {
878    let wda_inst: WebDrvAstn<GeckoDriver>;
879    let wda_setts = get_wda_setts(&mafa_in);
880    dbgg!(&wda_setts);
881    match WebDrvAstn::<GeckoDriver>::new(wda_setts) {
882        Ok(ret) => wda_inst = ret,
883        Err(err_wda) => match err_wda {
884            WdaError::WdcNotReady(WdcError::BadDrvCmd(err, msg), _) => {
885                if msg.contains("socksProxy is not a valid URL") {
886                    return Err(MafaError::InvalidSocks5Proxy);
887                } else {
888                    return Err(MafaError::WebDrvCmdRejected(err, msg));
889                }
890            }
891            WdaError::InvalidBrowserProfileId => return Err(MafaError::InvalidUseProfile),
892            WdaError::BrowserBinaryNotFound => return Err(MafaError::FirefoxNotFound),
893            _ => {
894                return Err(MafaError::UnexpectedWda(err_wda));
895            }
896        },
897    };
898
899    Ok(wda_inst)
900}