ass_parser/
lib.rs

1///! # AssParser
2///! 
3///! [ass_parser] is a crate to parse .ass (Advanced SubStation Alpha) files. which is a subtitle file for creating and displaying subtitles in video files. It is widely used due to it's complex text formatting, positioning and styling. The Advanced SubStation Alpha is a successor
4///! to the SubStation Alpha .ssa file.
5///! 
6///! ## Installation
7///! 
8///! Add `ass_parser` as a dependency to your cargo.toml:
9///! 
10///!  ```shell
11///!  cargo add ass_parser
12///!  ```
13///! # Introduction
14///! 
15///! AssParser is based on the principle of easy to read write and modify `.ass` files. This is the first version of `ass_parser`and now currently only have the features to modify `.ass` file.
16///! 
17///! # Example
18///! 
19/// Creating a simple `Advanced SubStation Alpha` `(.ass)` file with default values!
20///
21/// ```rust
22/// use ass_parser::{AssFile, ScriptInfo, V4Format, Events, AssFileOptions};
23/// use hex_color::HexColor;
24/// 
25/// fn main() {
26///     let mut ass_file = AssFile::new();
27///     let hexcolor = AssFileOptions::get_ass_color(HexColor::YELLOW);
28/// 
29///     ass_file.components.script
30///         .set_script(ScriptInfo::default());
31/// 
32///     ass_file.components.v4
33///         .set_v4(V4Format::default())
34///         .set_primarycolour(&hexcolor);
35/// 
36///     ass_file.components.events
37///         .set_events(Events::default());
38/// 
39///     AssFile::save_file(&ass_file, "new_subtitles.ass")
40/// 
41/// }
42/// 
43/// ```
44/// Here we create an .ass file with default values and When you open the .ass file you can see the
45/// following content.
46/// ```ass
47/// ScriptType: v4.00+
48/// PlayResX: 384
49/// PlayResY: 288
50/// ScaledBorderAndShadow: yes
51/// YCbCr Matrix: None
52/// 
53/// 
54/// [V4+ Styles]
55/// Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
56/// Style: Default,Arial,16,&H00ff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,1,0,2,10,10,10,1
57/// 
58/// 
59/// [Events]
60/// Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
61/// Dialogue: 0,0:00:00.00,0:00:01.00,Default,,0,0,0,,Hello Friend
62/// ```
63///
64/// # Add Dialogues
65///
66/// ```rust
67/// use ass_parser::{AssFile, ScriptInfo, V4Format, Events, AssFileOptions, Dialogue};
68/// use ass_parser::IndexNotFound;
69/// use hex_color::HexColor;
70/// 
71/// fn main() -> Result<(), IndexNotFound>{
72///     let mut ass_file = AssFile::new();
73///     let hexcolor = AssFileOptions::get_ass_color(HexColor::YELLOW);
74/// 
75///     let first_dialogue = Dialogue::default()
76///         .set_text("Hello There!")
77///         .set_start("0:00:00.10")
78///         .set_end("0:00:00.50");
79/// 
80///     let second_dialogue = Dialogue::default()
81///         .set_text("Hello Friend!")
82///         .set_start("00:00.50")
83///         .set_end("00:00.58");
84/// 
85///     let third_dialogue = Dialogue::default()
86///         .set_text("Hello World!!")
87///         .set_start("0:00:00.58")
88///         .set_end("0:00:01.01");
89/// 
90///     let events = Events::new()
91///         .add_first_dialogue(first_dialogue)?
92///         .add_dialogue(second_dialogue)
93///         .add_dialogue(third_dialogue)
94///         .create();
95/// 
96/// 
97///     ass_file.components.script
98///         .set_script(ScriptInfo::default())
99///         .set_scripttype("FFMPEG");
100/// 
101///     ass_file.components.v4
102///         .set_v4(V4Format::default())
103///         .set_primarycolour(&hexcolor);
104/// 
105///     ass_file.components.events
106///         .set_events(events);
107/// 
108///     AssFile::save_file(&ass_file, "new_subtitles.ass");
109/// 
110///     Ok(())
111/// 
112/// }
113/// ```
114///
115/// # Add Colors to Subtitles.
116///
117/// You can add individual colors to each subtitles using the `.set_colour()` function. This
118/// function takes HexColor. Make sure that you are using rand + std features to generate random colors via rand out of the box.
119///
120/// ```rust
121/// use hex_color::HexColor;
122/// use ass_parser::Dialogue;
123/// use ass_parser::Events;
124///
125/// let random_color:HexColor = rand::random();
126///
127/// let dialogue = Dialogue::default()
128///        .set_text("Hello Friend!")
129///        .set_start("0:00:00.50")
130///        .set_end("0:00:00.58")
131///        .set_colour(random_color);
132///
133/// let events = Events::new()
134///   .add_first_dialogue(dialogue).expect("Unable to add Dialogue");
135/// ```
136///
137/// # Modify Existing ASS files.
138///
139/// Use the `from_file` function of AssFile to modify and change the contents or appearance. 
140///
141/// ``` rust
142/// use ass_parser::{AssFile, Dialogue, AssFileOptions};
143/// use hex_color::HexColor;
144/// 
145/// fn main() -> Result<(), std::io::Error>{
146///     let mut ass_file = AssFile::from_file("./examples/subtitles.ass")?;
147///     let dialogue = Dialogue::default()
148///         .set_text("Hello Friend!");
149///     let primary_color = AssFileOptions::get_ass_color(HexColor::RED);
150/// 
151/// 
152///     ass_file.components.v4
153///         .set_primarycolour(&primary_color);
154///         
155///     ass_file.components.events
156///         .add_dialogue(dialogue);
157/// 
158///     AssFile::save_file(&ass_file, "new_subtitles.ass");
159/// 
160///     Ok(())
161/// }
162/// ```
163/// # Read Contents of ASS Files
164/// 
165/// To retrieve the values of fields present in each dialogue. You can load a `.ass` file and then access each dialogue details. 
166/// 
167/// ```rust
168/// use ass_parser::{AssFile, Dialogue};
169/// 
170/// let ass_file = AssFile::from_file("examples/subtitles.ass").expect("Unable to find file");
171/// let dialogues: Vec<Dialogue> = ass_file.events.get_dialogues();
172/// 
173/// for dialogue in dialogues {
174///     println!("layer: {:?}", &dialogue.get_layer());
175///     println!("name: {:?}", &dialogue.get_name());
176///     println!("end: {:?}", &dialogue.get_end());
177///     println!("start: {:?}", &dialogue.get_start());
178///     println!("text: {:?}", &dialogue.get_text());
179///     println!("marginl: {:?}", &dialogue.get_marginl());
180///     println!("marginr: {:?}", &dialogue.get_marginr());
181///     println!("marginv: {:?}", &dialogue.get_marginv());
182///     println!("style: {:?}", &dialogue.get_style());
183///     println!("effect: {:?}", &dialogue.get_effect());
184///     println!("colour: {:?}", &dialogue.get_colour());
185/// }
186/// ```
187///
188///
189/// # Added Support for SubRip files.
190///
191/// Now you can load `.srt` files and convert them to `.ass` files and even modify them on the
192/// process too. Here is an example from the `examples` directory.
193///
194/// In this example we load an SubRip file (`RapGod.srt`) and extract each subtitle from it and
195/// modify them by adding random colors to each subtitle. Then finally converting it to a `.ass`
196/// file and saving it.
197///
198/// ```rust
199/// use hex_color::HexColor;
200/// use ass_parser::{AssFile, AssFileOptions};
201/// use ass_parser::{ScriptInfo, V4Format, Events, Dialogue};
202/// use rand;
203/// 
204/// fn main() {
205///     let hexcolor = AssFileOptions::get_ass_color(HexColor::YELLOW);
206///     let srt_file = AssFile::from_srt("./examples/RapGod.srt");
207///     let mut ass_file = AssFile::new();
208///     let mut event = Events::default();
209/// 
210///     for srt_seg in srt_file.iter() {
211///         let start = &srt_seg.start;
212///         let end = &srt_seg.end;
213///         let text = &srt_seg.text;
214/// 
215///         let random_color:HexColor = rand::random();
216/// 
217///         let dialogue = Dialogue::default()
218///             .set_start(&start)
219///             .set_end(&end)
220///             .set_text(&text)
221///             .set_colour(random_color);
222/// 
223///         event.add_dialogue(dialogue);
224///     }
225///     
226/// 
227///     ass_file.components.script
228///         .set_script(ScriptInfo::default());
229/// 
230/// 
231/// 
232///     ass_file.components.v4
233///         .set_v4(V4Format::default())
234///         .set_primarycolour(&hexcolor);
235///     ass_file.components.events
236///         .set_events(event);
237/// 
238///     AssFile::save_file(&ass_file, "new_subtitle.ass");
239/// }
240/// ```
241///
242///
243/// ## This will generate an ASS file which would be similiar to this
244///
245/// ```ass
246///ScriptType: FFMPEG
247///PlayResX: 384
248///PlayResY: 288
249///ScaledBorderAndShadow: yes
250///YCbCr Matrix: None
251///
252///
253///[V4+ Styles]
254///Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
255///Style: Default,Arial,16,&H0ffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,1,0,2,10,10,10,1
256///
257///
258///[Events]
259///Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
260///Dialogue: 0,0:00:00.10,0:00:00.50,Default,,0,0,0,,Hello There!
261///Dialogue: 0,00:00.50,00:00.58,Default,,0,0,0,,Hello Friend!
262///Dialogue: 0,0:00:00.58,0:00:01.01,Default,,0,0,0,,Hello World!!
263/// ```
264/// # Events can also be created like this
265///
266///
267///```rust
268///use ass_parser::Dialogue;
269///use ass_parser::Events;
270///
271///let first_dialogue = Dialogue::default()
272///   .set_start("0:00:00.10")
273///   .set_end("0:00:00.50");
274///
275///let second_dialogue = Dialogue::default()
276///   .set_start("00:00.50")
277///   .set_end("00:00.58");
278///
279///let third_dialogue = Dialogue::default()
280///   .set_start("0:00:00.58")
281///   .set_end("0:00:01.01");
282///
283///let events = Events::new()
284///   .add_first_dialogue(first_dialogue).expect("Unable to add dialogue")
285///   .add_dialogue(second_dialogue)
286///   .add_dialogue(third_dialogue)
287///   .create();
288/// ```
289///
290/// You can burn this subtitle file to a video or use any video player to select a video file along
291/// with this subtitle file.
292///
293/// # Using [FFmpeg] to burn the video with the subtitles file.
294///
295/// You will first have to download and install [FFmpeg] on your system to try this. Once you have
296/// downloaded you can use the following command to burn the video file `video.avi` and the
297/// generated subtitle file `new_subtitles.ass` to a single output video file `output.avi`
298///
299/// ```shell
300/// ffmpeg -i video.avi -vf "ass=new_subtitles.ass" output.avi
301/// ```
302///! 
303///! [FFmpeg]: https://www.ffmpeg.org/about.html
304///! [ass_parser]: https://github.com/Aavtic/ass_parser
305
306
307use hex_color::HexColor;
308use std::{fs, io::Read};
309use std::io::{Seek, Write};
310use std::ops::Deref;
311use std::fmt;
312use std::iter::Iterator;
313
314mod parser;
315
316type SrtData = parser::SrtContent;
317
318const SCRIPT_HEADER:&str = "[Script Info]";
319const SCRIPT_TYPE:&str = "ScriptType: ";
320const SCRIPT_PLAYRESX:&str = "PlayResX: ";
321const SCRIPT_PLAYRESY:&str = "PlayResY: ";
322const SCRIPT_SCALEDBORDERANDSHADOW:&str =  "ScaledBorderAndShadow: ";
323const SCRIPT_YCBCR_MATRIX:&str =  "YCbCr Matrix: ";
324const V4_HEADER:&str = "[V4+ Styles]";
325const V4_STYLE_HEAD:&str = "Style: ";
326const EVENTS_HEADER:&str = "[Events]";
327const EVENT_HEAD:&str = "Dialogue: ";
328
329
330type Result<T> = std::result::Result<T, IndexNotFound>;
331
332#[derive(Debug, Clone)]
333pub struct IndexNotFound;
334
335impl std::fmt::Display for IndexNotFound {
336    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
337        write!(f, "The Index is not found on Dialogues.")
338    }
339}
340
341
342/// The First part of any Advanced SubStation Alpha file is `Script Info`.
343/// This holds necessary information which include the version the resolution of subtitles etc of
344/// the `.ass` file.
345
346#[derive(Debug, PartialEq, Clone)]
347pub struct ScriptInfo {
348    scripttype: Option<String>,
349    playresx: Option<String>,
350    playresy: Option<String>,
351    scaledborderandshadow: Option<String>,
352    ycbcr_matrix: Option<String>,
353}
354
355impl ScriptInfo {
356    fn get_key_values(&self) -> Vec<[&str; 2]> {
357        let mut values = Vec::new();
358
359        if let Some(value) = &self.scripttype {
360            values.push([SCRIPT_TYPE, value])
361        }
362        if let Some(value) = &self.playresx {
363            values.push([SCRIPT_PLAYRESX, value])
364        }
365        if let Some(value) = &self.playresy{
366            values.push([SCRIPT_PLAYRESY, value])
367        }
368        if let Some(value) = &self.scaledborderandshadow {
369            values.push([SCRIPT_SCALEDBORDERANDSHADOW, value])
370        }
371        if let Some(value) = &self.ycbcr_matrix {
372            values.push([SCRIPT_YCBCR_MATRIX, value])
373        }
374        values
375    }
376}
377
378impl ScriptInfo {
379    fn new() -> Self {
380        Self {
381    		scripttype: None,
382    		playresx: None,
383    		playresy: None,
384    		scaledborderandshadow: None,
385    		ycbcr_matrix: None,
386        }
387    }
388
389    pub fn set_script(&mut self, script: ScriptInfo) -> &mut ScriptInfo {
390        *self = script;
391        self
392    }
393}
394
395impl Default for ScriptInfo {
396    fn default() -> ScriptInfo {
397        ScriptInfo {
398    		scripttype: Some("v4.00+".to_string()),
399    		playresx: Some("384".to_string()),
400    		playresy: Some("288".to_string()),
401    		scaledborderandshadow: Some("yes".to_string()),
402    		ycbcr_matrix: Some("None".to_string()),
403        }
404    }
405}
406
407impl ScriptInfo {
408    /// After creating the `AssFile` set the scripttype of the .ass file.
409    /// If you want to specify any, the default ScriptType from the original `.ass` file will be
410    /// used.
411    /// This is the SSA script format version eg. "V4.00". It is used by SSA to give a warning if
412    /// you are using a version of SSA older than the version that created the script.
413    /// ASS version is “V4.00+”.
414    pub fn set_scripttype(&mut self, value: &str) -> &mut Self {
415        self.scripttype = Some(value.to_string());
416        self
417	  }
418    /// After creating the `AssFile` set the playresx of the .ass file.
419    ///
420    /// This is the height of the screen used by the script's author(s) when playing the script. SSA v4 will automatically select the nearest enabled setting, if you are using Directdraw playback.
421    ///
422    /// If you don't want to specify any, the default playresx from the original `.ass` file will be
423    /// used.
424    pub fn set_playresx(&mut self, value: &str) -> &mut Self {
425		self.playresx = Some(value.to_string());
426		self
427	}
428    /// After creating the `AssFile` set the playresy of the .ass file.
429    ///
430    /// This is the height of the screen used by the script's author(s) when playing the script. SSA v4 will automatically select the nearest enabled setting, if you are using Directdraw playback.
431    ///
432    /// If you want to specify any, the default playresy from the original `.ass` file will be
433    /// used.
434    /// 
435    pub fn set_playresy(&mut self, value: &str) -> &mut Self {
436		self.playresy = Some(value.to_string());
437		self
438	}
439    /// After creating the `AssFile` set the scaledborderandshadow of the .ass file.
440    /// If you want to specify any, the default scaledborderandshadowfrom the original `.ass` file will be
441    /// used.
442    pub fn set_scaledborderandshadow(&mut self, value: &str) -> &mut Self {
443		self.scaledborderandshadow = Some(value.to_string());
444		self
445	}
446    /// After creating the `AssFile` set the ycbcr_matrix( of the .ass file.
447    /// If you want to specify any, the default ycbcr_matrix from the original `.ass` file will be
448    /// used.
449    pub fn set_ycbcr_matrix(&mut self, value: &str) -> &mut Self {
450		self.ycbcr_matrix = Some(value.to_string());
451		self
452	}
453}
454
455
456/// # V4Format
457///
458/// The Second part of any Advanced SubStation Alpha file is `V4Format`.
459/// This is the part which has fields separated by comma which specify the format, styling,
460/// encoding colors and many other important parts of the the `.ass` file.
461
462#[derive(Debug, PartialEq, Clone)]
463pub struct V4Format {
464    name: Option<String>,
465    fontname: Option<String>,
466    fontsize: Option<String>,
467    primarycolour: Option<String>,
468    secondarycolour: Option<String>,
469    outlinecolour: Option<String>,
470    backcolour: Option<String>,
471    bold: Option<String>,
472    italic: Option<String>,
473    underline: Option<String>,
474    strikeout: Option<String>,
475    scalex: Option<String>,
476    scaley: Option<String>,
477    spacing: Option<String>,
478    angle: Option<String>,
479    borderstyle: Option<String>,
480    outline: Option<String>,
481    shadow: Option<String>,
482    alignment: Option<String>,
483    marginl: Option<String>,
484    marginr: Option<String>,
485    marginv: Option<String>,
486    encoding: Option<String>,
487}
488
489impl V4Format {
490    fn new() -> V4Format {
491        Self {
492            name: None,
493            fontname: None,
494            fontsize: None,
495            primarycolour: None,
496            secondarycolour: None,
497            outlinecolour: None,
498            backcolour: None,
499            bold: None,
500            italic: None,
501            underline: None,
502            strikeout: None,
503            scalex: None,
504            scaley: None,
505            spacing: None,
506            angle: None,
507            borderstyle: None,
508            outline: None,
509            shadow: None,
510            alignment: None,
511            marginl: None,
512            marginr: None,
513            marginv: None,
514            encoding: None,
515        }
516    }
517}
518
519impl Default for V4Format {
520    /// V4 Set with the common '`Default`' Format for `Advanced SubStation Alpha`.
521    fn default() -> V4Format {
522        V4Format {
523        name: Some("Default".to_string()),
524        fontname: Some("Arial".to_string()),
525        fontsize: Some("16".to_string()),
526        primarycolour: Some("&Hffffff".to_string()),
527        secondarycolour: Some("&Hffffff".to_string()),
528        outlinecolour:Some("&H0".to_string()),
529        backcolour: Some("&H0".to_string()),
530        bold: Some("0".to_string()),
531        italic: Some("0".to_string()),
532        underline: Some("0".to_string()),
533        strikeout: Some("0".to_string()),
534        scalex: Some("100".to_string()),
535        scaley: Some("100".to_string()),
536        spacing: Some("0".to_string()),
537        angle:Some("0".to_string()),
538        borderstyle:Some("1".to_string()),
539        outline: Some("1".to_string()),
540        shadow: Some("0".to_string()),
541        alignment: Some("2".to_string()),
542        marginl:Some("10".to_string()),
543        marginr: Some("10".to_string()),
544        marginv: Some("10".to_string()),
545        encoding: Some("1".to_string()),
546        }
547    }
548}
549
550impl V4Format {
551    /// Set V4 from a V4 Struct.
552    pub fn set_v4(&mut self, v4: V4Format) -> &mut V4Format {
553        *self = v4;
554        self
555    }
556    fn get_array(&self) -> [&Option<String>; 23] {
557        [
558            &self.name,
559            &self.fontname,
560            &self.fontsize,
561            &self.primarycolour,
562            &self.secondarycolour,
563            &self.outlinecolour,
564            &self.backcolour,
565            &self.bold,
566            &self.italic,
567            &self.underline,
568            &self.strikeout,
569            &self.scalex,
570            &self.scaley,
571            &self.spacing,
572            &self.angle,
573            &self.borderstyle,
574            &self.outline,
575            &self.shadow,
576            &self.alignment,
577            &self.marginl,
578            &self.marginr,
579            &self.marginv,
580            &self.encoding,
581            ]
582    }
583
584}
585
586impl V4Format {
587    // Ik this looks crazy. but what do?
588    /// set the name for the V4 field.
589    /// The name of the Style. Case sensitive. Cannot include commas
590	pub fn set_name(&mut self,
591                    value: &str) -> &mut Self{
592        self.name = Some(value.to_string());
593        self
594	}
595    /// set the fontname for the V4 field.
596    /// The fontname as used by Windows. Case-sensitive.
597        pub fn set_fontname(&mut self,
598                        value: &str) -> &mut Self{
599        self.fontname = Some(value.to_string());
600        self
601	}
602    /// set the fontsize for the V4 field.
603	pub fn set_fontsize(&mut self,
604                        value: &str) -> &mut Self{
605        self.fontsize = Some(value.to_string());
606        self
607	}
608    /// set the primarycolour for the V4 field.
609    /// ```rust
610    /// use ass_parser::{AssFile, ScriptInfo, V4Format, Events, AssFileOptions};
611    /// use hex_color::HexColor;
612    /// 
613    /// fn main() {
614    ///     let mut ass_file = AssFile::new();
615    ///     let hexcolor = AssFileOptions::get_ass_color(HexColor::YELLOW);
616    /// 
617    ///     ass_file.components.script
618    ///         .set_script(ScriptInfo::default());
619    /// 
620    ///     ass_file.components.v4
621    ///         .set_v4(V4Format::default())
622    ///         .set_primarycolour(&hexcolor);
623    /// 
624    ///     ass_file.components.events
625    ///         .set_events(Events::default());
626    /// 
627    ///     AssFile::save_file(&ass_file, "new_subtitles.ass")
628    /// }
629    /// ```
630	pub fn set_primarycolour(&mut self,
631                             value: &str) -> &mut Self{
632        self.primarycolour = Some(value.to_string());
633        self
634	}
635    /// set the secondarycolour for the V4 field.
636    /// ```rust
637    /// use ass_parser::{AssFile, ScriptInfo, V4Format, Events, AssFileOptions};
638    /// use hex_color::HexColor;
639    /// 
640    /// fn main() {
641    ///     let mut ass_file = AssFile::new();
642    ///     let hexcolor = AssFileOptions::get_ass_color(HexColor::YELLOW);
643    /// 
644    ///     ass_file.components.script
645    ///         .set_script(ScriptInfo::default());
646    /// 
647    ///     ass_file.components.v4
648    ///         .set_v4(V4Format::default())
649    ///         .set_secondarycolour(&hexcolor);
650    /// 
651    ///     ass_file.components.events
652    ///         .set_events(Events::default());
653    /// 
654    ///     AssFile::save_file(&ass_file, "new_subtitles.ass")
655    /// }
656    /// ```
657	pub fn set_secondarycolour(&mut self,
658                               value: &str) -> &mut Self{
659        self.secondarycolour = Some(value.to_string());
660        self
661	}
662    /// set the outlinecolour for the V4 field.
663    /// ```rust
664    /// use ass_parser::{AssFile, ScriptInfo, V4Format, Events, AssFileOptions};
665    /// use hex_color::HexColor;
666    /// 
667    /// fn main() {
668    ///     let mut ass_file = AssFile::new();
669    ///     let hexcolor = AssFileOptions::get_ass_color(HexColor::YELLOW);
670    /// 
671    ///     ass_file.components.script
672    ///         .set_script(ScriptInfo::default());
673    /// 
674    ///     ass_file.components.v4
675    ///         .set_v4(V4Format::default())
676    ///         .set_outlinecolour(&hexcolor);
677    /// 
678    ///     ass_file.components.events
679    ///         .set_events(Events::default());
680    /// 
681    ///     AssFile::save_file(&ass_file, "new_subtitles.ass")
682    /// }
683    /// ```
684	pub fn set_outlinecolour(&mut self,
685                             value: &str) -> &mut Self{
686        self.outlinecolour = Some(value.to_string());
687        self
688	}
689    /// set the backcolour for the V4 field.
690    /// ```rust
691    /// use ass_parser::{AssFile, ScriptInfo, V4Format, Events, AssFileOptions};
692    /// use hex_color::HexColor;
693    /// 
694    /// fn main() {
695    ///     let mut ass_file = AssFile::new();
696    ///     let hexcolor = AssFileOptions::get_ass_color(HexColor::YELLOW);
697    /// 
698    ///     ass_file.components.script
699    ///         .set_script(ScriptInfo::default());
700    /// 
701    ///     ass_file.components.v4
702    ///         .set_v4(V4Format::default())
703    ///         .set_backcolour(&hexcolor);
704    /// 
705    ///     ass_file.components.events
706    ///         .set_events(Events::default());
707    /// 
708    ///     AssFile::save_file(&ass_file, "new_subtitles.ass")
709    /// }
710    /// ```
711	pub fn set_backcolour(&mut self,
712                          value: &str) -> &mut Self{
713        self.backcolour = Some(value.to_string());
714        self
715	}
716    /// set the bold for the V4 field.
717    /// This defines whether text is bold (true) or not (false). -1 is True, 0 is False. This is independant of the Italic attribute - you can have have text which is both bold and italic
718	pub fn set_bold(&mut self,
719                    value: &str) -> &mut Self{
720        self.bold = Some(value.to_string());
721        self
722	}
723    /// set the italic for the V4 field.
724    /// This defines whether text is italic (true) or not (false). -1 is True, 0 is False. This is independant of the bold attribute - you can have have text which is both bold and italic.
725	pub fn set_italic(&mut self,
726                      value: &str) -> &mut Self{
727        self.italic = Some(value.to_string());
728        self
729	}
730    /// set the underline for the V4 field.
731    ///  use either of [-1 or 0] where -1 is considered True and 0 is considered False.
732	pub fn set_underline(&mut self,
733                         value: &str) -> &mut Self{
734        self.underline = Some(value.to_string());
735        self
736	}
737    /// set the strikeout for the V4 field.
738    ///  use either of [-1 or 0] where -1 is considered True and 0 is considered False.
739	pub fn set_strikeout(&mut self,
740                         value: &str) -> &mut Self{
741        self.strikeout = Some(value.to_string());
742        self
743	}
744    /// set the scalex for the V4 field.
745    /// ScaleX. Modifies the width of the font. [percent]
746	pub fn set_scalex(&mut self,
747                      value: &str) -> &mut Self{
748        self.scalex = Some(value.to_string());
749        self
750	}
751    /// set the scaley for the V4 field.
752    /// ScaleX. Modifies the height of the font. [percent]
753	pub fn set_scaley(&mut self,
754                      value: &str) -> &mut Self{
755        self.scaley = Some(value.to_string());
756        self
757	}
758    /// set the spacing for the V4 field.
759    ///  Extra space between characters. [pixels]
760	pub fn set_spacing(&mut self,
761                       value: &str) -> &mut Self{
762        self.spacing = Some(value.to_string());
763        self
764	}
765    /// set the angle for the V4 field.
766    /// The origin of the rotation is defined by the alignment. Can be a floating point number. [degrees]
767	pub fn set_angle(&mut self,
768                     value: &str) -> &mut Self{
769        self.angle = Some(value.to_string());
770        self
771	}
772    /// set the borderstyle for the V4 field.
773    ///  pass either 1 or 3. where 1=Outline + drop shadow, 3=Opaque box.
774	pub fn set_borderstyle(&mut self,
775                           value: &str) -> &mut Self{
776        self.borderstyle = Some(value.to_string());
777        self
778	}
779    /// set the outline for the V4 field.
780    /// If BorderStyle is 1,  then this specifies the width of the outline around the text, in pixels.
781    /// Values may be 0, 1, 2, 3 or 4.
782	pub fn set_outline(&mut self,
783                       value: &str) -> &mut Self{
784        self.outline = Some(value.to_string());
785        self
786	}
787    /// set the shadow for the V4 field.
788    /// If BorderStyle is 1,  then this specifies the depth of the drop shadow behind the text, in pixels. Values may be 0, 1, 2, 3 or 4. Drop shadow is always used in addition to an outline. 
789	pub fn set_shadow(&mut self,
790                      value: &str) -> &mut Self{
791        self.shadow = Some(value.to_string());
792        self
793	}
794    /// set the alignment for the V4 field.
795    /// This sets how text is "justified" within the Left/Right onscreen margins, and also the vertical placing. Values may be 1=Left, 2=Centered, 3=Right. Add 4 to the value for a "Toptitle". Add 8 to the value for a "Midtitle".
796    /// eg. 5 = left-justified toptitle
797	pub fn set_alignment(&mut self,
798                         value: &str) -> &mut Self{
799        self.alignment = Some(value.to_string());
800        self
801	}
802    /// set the marginl for the V4 field.
803    /// This defines the Left Margin in pixels. It is the distance from the left-hand edge of the screen.The three onscreen margins (MarginL, MarginR, MarginV) define areas in which the subtitle text will be displayed.
804	pub fn set_marginl(&mut self,
805                       value: &str) -> &mut Self{
806        self.marginl = Some(value.to_string());
807        self
808	}
809    /// set the marginr for the V4 field.
810    /// This defines the Right Margin in pixels. It is the distance from the right-hand edge of the screen. The three onscreen margins (MarginL, MarginR, MarginV) define areas in which the subtitle text will be displayed.
811	pub fn set_marginr(&mut self,
812                       value: &str) -> &mut Self{
813        self.marginr = Some(value.to_string());
814        self
815	}
816    /// set the marginv for the V4 field.
817    /// This defines the vertical Left Margin in pixels.
818    /// For a subtitle, it is the distance from the bottom of the screen.
819    /// For a toptitle, it is the distance from the top of the screen.
820    /// For a midtitle, the value is ignored - the text will be vertically centred.
821	pub fn set_marginv(&mut self,
822                       value: &str) -> &mut Self{
823        self.marginv = Some(value.to_string());
824        self
825	}
826    /// set the encoding for the V4 field.
827    /// This specifies the font character set or encoding and on multi-lingual Windows installations it provides access to characters used in multiple than one languages. It is usually 0 (zero) for English (Western, ANSI) Windows.
828	fn set_encoding(&mut self, value: &str) -> &mut Self{
829        self.encoding = Some(value.to_string());
830        self
831	}
832}
833
834
835/// # Events
836/// In `Advanced SubStation Alpha` Events is the core part of the subtitle file.
837/// This contains Dialogues which can be subtitle text. and even Graphics.
838
839#[derive(Debug, PartialEq, Clone)]
840pub struct Events {
841    pub dialogues: Dialogues,
842}
843
844impl Events {
845    /// Returns a Clone of `Dialogues`
846    /// You can then use this to access fields of `Dialogue`.
847    /// ```rust 
848    /// 
849    /// use ass_parser::AssFile;
850    ///
851    /// let mut ass_file = ass_parser::AssFile::from_file("./examples/subtitles.ass").expect("error while reading file.");
852    /// let dialogues = ass_file.events.get_dialogues().clone();
853
854    /// for dialogue in dialogues {
855    ///     println!("layer: {:?}", &dialogue.get_layer());
856    ///     println!("name: {:?}", &dialogue.get_name());
857    ///     println!("end: {:?}", &dialogue.get_end());
858    ///     println!("start: {:?}", &dialogue.get_start());
859    ///     println!("text: {:?}", &dialogue.get_text());
860    ///     println!("marginl: {:?}", &dialogue.get_marginl());
861    ///     println!("marginr: {:?}", &dialogue.get_marginr());
862    ///     println!("marginv: {:?}", &dialogue.get_marginv());
863    ///     println!("style: {:?}", &dialogue.get_style());
864    ///     println!("effect: {:?}", &dialogue.get_effect());
865    ///     println!("colour: {:?}", &dialogue.get_colour());
866    /// }
867    /// ```
868    pub fn get_dialogues(&self) -> Vec<Dialogue> {
869        return self.dialogues.dialogues.clone();
870    }
871}
872
873impl Events {
874    /// Create a new instance of Event.
875    /// This will have `None` for all the fields for EventFormat. 
876    pub fn new() -> Events {
877        let dialogue = Dialogue::new();
878        Events {
879            dialogues: 
880                Dialogues {
881                    dialogues: 
882                        vec![
883                            dialogue
884                        ]
885                }
886        }
887    }
888
889   /// Create the final Event
890   /// This simply consumes the mutable self and returns self.
891   /// Call this function at the end of constructing an `Event`.
892   ///
893   /// # Example
894   /// ```rust
895   ///  use ass_parser::Dialogue;
896   ///  use ass_parser::Events;
897   ///
898   ///  let dialogue = Dialogue::default();
899   ///  let events = Events::new()
900   ///     .add_first_dialogue(dialogue.clone().set_text("Hello There!")).unwrap()
901   ///     .add_dialogue(dialogue.clone().set_text("Hello Friend!"))
902   ///     .add_n_dialogue(1, dialogue.clone().set_text("Hello Friend :)")).unwrap()
903   ///     .add_last_dialogue(dialogue.set_text("Bye Friend.")).unwrap()
904   ///     .create();
905   /// ```
906
907    pub fn create(&mut self) -> Self {
908        self.clone()
909    }
910
911    /// Add a dialogue to the first of the `Events` Struct.
912    /// # Example
913    /// ```rust
914    /// use ass_parser::Dialogue;
915    /// use ass_parser::Events;
916    ///
917   ///  let dialogue = Dialogue::default();
918   ///  let events = Events::new()
919   ///     .add_first_dialogue(dialogue.set_text("Hello There!")).unwrap();
920   /// ```
921   /// 
922    pub fn add_first_dialogue(&mut self, dialogue: Dialogue) -> Result<&mut Self> {
923        match self.dialogues.dialogues.first_mut() {
924            Some(dlg) => {
925                *dlg = dialogue;
926                Ok(self)
927            },
928            None  => {
929                Err(IndexNotFound)
930            }
931        }
932    }
933
934    /// Add a dialogue to the last of the `Events` Struct.
935    /// # Example
936    /// ```rust
937    /// use ass_parser::Dialogue;
938    /// use ass_parser::Events;
939    ///
940   ///  let dialogue = Dialogue::default();
941   ///  let events = Events::new()
942   ///     .add_last_dialogue(dialogue.set_text("Hello There!")).unwrap();
943   /// ```
944    pub fn add_last_dialogue(&mut self, dialogue: Dialogue) -> Result<&mut Self> {
945        match self.dialogues.dialogues.last_mut() {
946            Some(dlg) => {
947                *dlg = dialogue;
948                Ok(self)
949            },
950            None  => {
951                Err(IndexNotFound)
952            }
953        }
954    }
955
956    /// Add a dialogue to the nth position of the `Events` Struct.
957    /// # Example
958    /// ```rust
959    /// use ass_parser::Dialogue;
960    /// use ass_parser::Events;
961    ///
962   ///  let dialogue = Dialogue::default();
963   ///  let events = Events::new()
964   ///     .add_n_dialogue(0, dialogue.set_text("Hello There!")).unwrap();
965   /// ```
966    pub fn add_n_dialogue(&mut self, n: usize, dialogue: Dialogue) -> Result<&mut Self> {
967        match self.dialogues.dialogues.get_mut(n) {
968            Some(dlg) => {
969                *dlg = dialogue;
970                Ok(self)
971            },
972            None  => {
973                Err(IndexNotFound)
974            }
975        }
976    }
977
978    /// Add a dialogue to the end of the `Events` Struct.
979    /// # Example
980    /// ```rust
981    /// use ass_parser::Dialogue;
982    /// use ass_parser::Events;
983    ///
984   ///  let dialogue = Dialogue::default();
985   ///  let mut events = Events::new();
986   ///  events.add_dialogue(dialogue.set_text("Hello There!"));
987   /// ```
988    pub fn add_dialogue(&mut self, dialogue: Dialogue) -> &mut Events {
989        self.dialogues.dialogues.push(dialogue);
990        self
991    }
992}
993
994impl Default for Events {
995    fn default() -> Events {
996        Events {
997            dialogues: 
998                Dialogues {
999                        dialogues: vec![
1000                        Dialogue {
1001                            event: EventFormat::default(),
1002                        }
1003                    ]
1004                }
1005        }
1006    }
1007}
1008
1009
1010impl Events {
1011    pub fn set_events(&mut self, events: Events) -> &mut Events {
1012        *self = events;
1013        self
1014    }
1015}
1016
1017/// # Dialogues
1018/// This stores each `Dialogue: ` field in an `Advanced SubStation File`
1019#[derive(Debug, PartialEq,Clone)]
1020pub struct Dialogues {
1021    pub dialogues: Vec<Dialogue>
1022}
1023
1024/// A single `Dialogue` which contain `event` which can be used to modify the state of a
1025/// `Dialogue`.
1026#[derive(Debug, PartialEq, Clone)]
1027pub struct Dialogue {
1028    event: EventFormat
1029}
1030
1031#[derive(Debug, PartialEq,Clone)]
1032struct EventFormat {
1033    layer: Option<String>,
1034    start: Option<String>,
1035    end: Option<String>,
1036    style: Option<String>,
1037    name: Option<String>,
1038    marginl: Option<String>,
1039    marginr: Option<String>,
1040    marginv: Option<String>,
1041    effect: Option<String>,
1042    text: Option<String>,
1043    color: Option<String>
1044}
1045
1046impl Default for EventFormat {
1047    fn default() -> EventFormat {
1048        EventFormat {
1049            layer: Some("0".to_string()),
1050            start: Some("0:00:00.00".to_string()),
1051            end: Some("0:00:00.00".to_string()),
1052            style: Some("Default".to_string()),
1053            name: Some("".to_string()),
1054            marginl: Some("0".to_string()),
1055            marginr: Some("0".to_string()),
1056            marginv: Some("0".to_string()),
1057            effect: Some("".to_string()),
1058            text: None,
1059            color: None,
1060        }
1061    }
1062}
1063
1064impl Dialogue {
1065    pub fn new() -> Self {
1066        Self {
1067            event: EventFormat {
1068                layer: None,
1069                start: None,
1070                end: None,
1071                style: None,
1072                name: None,
1073                marginl: None,
1074                marginr: None,
1075                marginv: None,
1076                effect: None,
1077                text: None,
1078                color: None,
1079            }
1080        }
1081    }
1082}
1083
1084impl Default for Dialogue {
1085    fn default() -> Dialogue {
1086        Dialogue {
1087            event: EventFormat::default()
1088        }
1089    }
1090}
1091
1092impl Dialogue {
1093    fn to_string(&self) -> String {
1094        let mut dialogue_string = String::new();
1095        dialogue_string.push_str(EVENT_HEAD);
1096        dialogue_string.push_str(&(self.event.layer.as_ref().unwrap_or(&"".to_owned()).to_owned() + ","));
1097        dialogue_string.push_str(&(self.event.start.as_ref().unwrap_or(&"".to_owned()).to_owned() + ","));
1098        dialogue_string.push_str(&(self.event.end.as_ref().unwrap_or(&"".to_owned()).to_owned() + ","));
1099        dialogue_string.push_str(&(self.event.style.as_ref().unwrap_or(&"".to_owned()).to_owned() + ","));
1100        dialogue_string.push_str(&(self.event.name.as_ref().unwrap_or(&"".to_owned()).to_owned() + ","));
1101        dialogue_string.push_str(&(self.event.marginl.as_ref().unwrap_or(&"".to_owned()).to_owned() + ","));
1102        dialogue_string.push_str(&(self.event.marginr.as_ref().unwrap_or(&"".to_owned()).to_owned() + ","));
1103        dialogue_string.push_str(&(self.event.marginv.as_ref().unwrap_or(&"".to_owned()).to_owned() + ","));
1104        dialogue_string.push_str(&(self.event.effect.as_ref().unwrap_or(&"".to_owned()).to_owned() + ","));
1105        dialogue_string.push_str(&(self.event.text.as_ref().unwrap_or(&"".to_owned()).to_owned() + "\n"));
1106
1107        return dialogue_string;
1108    }
1109}
1110
1111impl Dialogue {
1112    /// set the layer
1113    /// Layer (any integer)
1114    /// Subtitles having different layer number will be ignored during the collusion detection.
1115    /// Higher numbered layers will be drawn over the lower numbered.
1116    pub fn set_layer(mut self, value: &str) -> Self {
1117		self.event.layer = Some(value.to_string());
1118		self
1119	}
1120    /// set the start time of the subtitle.
1121    /// Start Time of the Event, in 0:00:00:00 format ie. Hrs:Mins:Secs:hundredths. This is the time elapsed during script playback at which the text will appear onscreen. Note that there is a single digit for the hours!
1122    pub fn set_start(mut self, value: &str) -> Self {
1123		self.event.start = Some(value.to_string());
1124		self
1125    }
1126	/// set the end time of the subtitle.
1127    ///  End Time of the Event, in 0:00:00:00 format ie. Hrs:Mins:Secs:hundredths. This is the time elapsed during script playback at which the text will disappear offscreen. Note that there is a single digit for the hours!
1128    pub fn set_end(mut self, value: &str) -> Self {
1129		self.event.end = Some(value.to_string());
1130		self
1131	}
1132    /// set the style.
1133    /// Style name. If it is "Default", then your own *Default style will be subtituted.
1134    ///However, the Default style used by the script author IS stored in the script even though SSA ignores it - so if you want to use it, the information is there - you could even change the Name in the Style definition line, so that it will appear in the list of "script" styles.
1135    pub fn set_style(mut self, value: &str) -> Self {
1136		self.event.style = Some(value.to_string());
1137		self
1138	}
1139    /// set name.
1140    ///  Character name. This is the name of the character who speaks the dialogue. It is for information only, to make the script is easier to follow when editing/timing.
1141    pub fn set_name(mut self, value: &str) -> Self {
1142		self.event.name = Some(value.to_string());
1143		self
1144	}
1145    /// set the marginl
1146    /// 4-figure Left Margin override. The values are in pixels. All zeroes means the default margins defined by the style are used.
1147    pub fn set_marginl(mut self, value: &str) -> Self {
1148		self.event.marginl = Some(value.to_string());
1149		self
1150	}
1151    /// set the marginr
1152    ///  4-figure Right Margin override. The values are in pixels. All zeroes means the default margins defined by the style are used.
1153    pub fn set_marginr(mut self, value: &str) -> Self {
1154		self.event.marginr = Some(value.to_string());
1155		self
1156	}
1157    /// set the marginv
1158    ///  4-figure Bottom Margin override. The values are in pixels. All zeroes means the default margins defined by the style are used.
1159    pub fn set_marginv(mut self, value: &str) -> Self {
1160		self.event.marginv = Some(value.to_string());
1161		self
1162	}
1163    /// set effects for the Dialogue object.
1164    /// Transition Effect. This is either empty, or contains information for one of the three transition effects implemented in SSA v4.x
1165    /// The effect names are case sensitive and must appear exactly as shown. The effect names do not have quote marks around them.
1166    /// "Karaoke" means that the text will be successively highlighted one word at a time.
1167    /// Karaoke as an effect type is obsolete.
1168    pub fn set_effect(mut self, value: &str) -> Self {
1169		self.event.effect = Some(value.to_string());
1170		self
1171	}
1172    /// set the text for the subtitle.
1173    /// Subtitle Text. This is the actual text which will be displayed as a subtitle onscreen. Everything after the 9th comma is treated as the subtitle text, so it can include commas.
1174    /// The text can include \n codes which is a line break, and can include Style Override control codes, which appear between braces { }.
1175    pub fn set_text(mut self, value: &str) -> Self {
1176		self.event.text = Some(value.to_string());
1177		self
1178	}
1179
1180    /// set the color of the subtitle.
1181    pub fn set_colour(mut self, color: HexColor) -> Self {
1182        let colour = AssFileOptions::get_ass_color_text(color);
1183        match &self.event.text {
1184            Some(text) => {
1185                let new_text = &(colour.clone() + text);
1186                // If you know a better way, please do create a pull request
1187                self = self.clone().set_text(new_text);
1188                self.event.color = Some(colour);
1189                self
1190            },
1191            None => {
1192                self.event.color = Some(colour.clone());
1193                self.set_text(&colour)
1194            }
1195        }
1196    }
1197}
1198
1199
1200impl Dialogue {
1201    /// get the layer of the subtitle
1202    pub fn get_layer(&self) -> Option<String> {
1203				return self.event.layer.clone();
1204	}
1205    /// get the start time of the `Dialogue`
1206    /// Start Time of the Event, in 0:00:00:00 format ie. Hrs:Mins:Secs:hundredths. This is the time elapsed during script playback at which the text will appear onscreen. Note that there is a single digit for the hours!
1207    pub fn get_start(&self) -> Option<String> {
1208				return self.event.start.clone();
1209    }
1210	/// get the end time of the `Dialogue`.
1211    ///  End Time of the Event, in 0:00:00:00 format ie. Hrs:Mins:Secs:hundredths. This is the time elapsed during script playback at which the text will disappear offscreen. Note that there is a single digit for the hours!
1212    pub fn get_end(&self) -> Option<String> {
1213				return self.event.end.clone();
1214	}
1215    /// get the style of the `Dialogue`.
1216    pub fn get_style(&self) -> Option<String> {
1217				return self.event.style.clone();
1218	}
1219    /// get the name of the `Dialogue`.
1220    ///  Character name. This is the name of the character who speaks the dialogue. It is for information only, to make the script is easier to follow when editing/timing.
1221    pub fn get_name(&self) -> Option<String> {
1222				return self.event.name.clone();
1223	}
1224    /// get the marginl of the `Dialogue`.
1225    /// 4-figure Left Margin override. The values are in pixels. All zeroes means the default margins defined by the style are used.
1226    pub fn get_marginl(&self) -> Option<String> {
1227				return self.event.marginl.clone();
1228	}
1229    /// get the marginr of the `Dialogue`.
1230    ///  4-figure Right Margin override. The values are in pixels. All zeroes means the default margins defined by the style are used.
1231    pub fn get_marginr(&self) -> Option<String> {
1232				return self.event.marginr.clone();
1233	}
1234    /// get the marginv of the `Dialogue`.
1235    ///  4-figure Bottom Margin override. The values are in pixels. All zeroes means the default margins defined by the style are used.
1236    pub fn get_marginv(&self) -> Option<String> {
1237				return self.event.marginv.clone();
1238	}
1239    /// get the effects for the Dialogue object.
1240    /// Transition Effect. This is either empty, or contains information for one of the three transition effects implemented in SSA v4.x
1241    /// The effect names are case sensitive and must appear exactly as shown. The effect names do not have quote marks around them.
1242    /// "Karaoke" means that the text will be successively highlighted one word at a time.
1243    /// Karaoke as an effect type is obsolete.
1244    pub fn get_effect(&self) -> Option<String> {
1245				return self.event.effect.clone();
1246	}
1247    /// get the text for the subtitle.
1248    /// Subtitle Text. This is the actual text which will be displayed as a subtitle onscreen. Everything after the 9th comma is treated as the subtitle text, so it can include commas.
1249    /// The text can include \n codes which is a line break, and can include Style Override control codes, which appear between braces { }.
1250    pub fn get_text(&self) -> Option<String> {
1251				return self.event.text.clone();
1252	}
1253
1254    /// get the color of the subtitle.
1255    pub fn get_colour(&self) -> Option<String> {
1256        return self.event.color.clone()
1257    }
1258}
1259
1260pub struct AssFileOptions{}
1261
1262/// `script`, `v4` and `event` are fields in `Components`
1263#[derive(Clone, PartialEq, Debug)]
1264pub struct Components {
1265    /// instance holding the scirpt field.
1266    pub script: ScriptInfo,
1267    /// instance holding the V4 field of.
1268    pub v4: V4Format,
1269    /// instance holding the Events field of.
1270    pub events: Events,
1271}
1272
1273
1274pub struct Srt {
1275    srt_data: SrtData,
1276}
1277
1278impl Srt {
1279    pub fn iter(&self) -> std::slice::Iter<'_, parser::SrtData> {
1280        let iterator = self.srt_data.iter();
1281        return iterator;
1282    }
1283}
1284
1285
1286/// # AssFile represents an instance of an existing `.ass` file.
1287///  The `AssFile::from_file function can be used to construct an `AssFile` from an existing `.ass
1288///  file`.
1289
1290#[derive(Clone, PartialEq,Debug)]
1291pub struct AssFile{
1292    _ass_file: String,
1293    /// Each components present in a `.ass` file. 
1294    /// They are `script` `v4` and `events`.
1295    pub components: Components,
1296}
1297
1298impl Deref for AssFile {
1299    type Target = Components;
1300
1301    fn deref(&self) -> &Self::Target {
1302        &self.components
1303    }
1304}
1305
1306impl AssFile {
1307    pub fn new() -> AssFile {
1308        AssFile {
1309            _ass_file: String::new(),
1310            components: Components {
1311                script: ScriptInfo::new(),
1312                v4: V4Format::new(),
1313                events: Events::new(),
1314            }
1315        }
1316    }
1317
1318    /// Load Subtitles from a SubRip file.
1319    ///
1320    /// # Example
1321    /// ```rust
1322    /// use ass_parser::AssFile;
1323    ///
1324    /// let srt_file = AssFile::from_srt("./examples/RapGod.srt");
1325    ///
1326    /// for srt_seg in srt_file.iter() {
1327    ///    let start = &srt_seg.start;
1328    ///    let end = &srt_seg.end;
1329    ///    let text = &srt_seg.text;
1330    ///
1331    ///    println!("Start: {}\nEnd: {}\nText: {}", start, end, text);
1332    ///}
1333    ///```
1334    pub fn from_srt(filename: &str) -> Srt {
1335        let file_contents = get_contents(filename).unwrap();
1336        let srtdata = parser::SrtData::new();
1337        let srt = srtdata.parse_srt(file_contents);
1338
1339        Srt {
1340            srt_data: srt,
1341        }
1342    }
1343}
1344
1345struct Parser; 
1346impl Parser {
1347    fn new() -> Parser {
1348        Parser
1349    }
1350
1351    fn stringify_script(&self, scriptinfo: Vec<[&str; 2]>) -> String {
1352        let mut contents = String::new();
1353
1354        for pair in scriptinfo {
1355            contents.push_str(&(pair[0].to_owned() + pair[1] + "\n"))
1356        }
1357        contents
1358    }
1359
1360    fn combine_components(&self, components: &Components) -> String {
1361        let components = components.clone();
1362        let script = components.script;
1363        let v4 = components.v4;
1364        let events = components.events;
1365        let scriptinfo  = script.get_key_values();
1366
1367        let script_data = &self.stringify_script(scriptinfo);
1368        let v4_data = &self.plug_v4(v4);
1369        let event_data = &self.plug_events(events);
1370        let total_data = format!("{}\n\n{}\n\n{}", script_data, v4_data, event_data);
1371
1372        return total_data;
1373    }
1374
1375    fn _plug_script(&self, script_lines: Vec<String>, scriptinfo: ScriptInfo) -> String {
1376        let mut new_lines = script_lines.clone();
1377        let mut total_lines = String::new();
1378        let script_type = scriptinfo.scripttype.unwrap();
1379        let playresx = scriptinfo.playresx.unwrap();
1380        let playresy = scriptinfo.playresy.unwrap();
1381        let scaledborderandshadow = scriptinfo.scaledborderandshadow.unwrap();
1382        let ycbcr_matrix = scriptinfo.ycbcr_matrix.unwrap();
1383
1384
1385        for (i, line) in script_lines.iter().enumerate() {
1386            if line.starts_with(SCRIPT_TYPE) {
1387                new_lines[i] = line[..SCRIPT_TYPE.len()].to_owned() + &script_type + "\n";
1388                continue
1389            } else if line.starts_with(SCRIPT_PLAYRESX){
1390                new_lines[i] = line[..SCRIPT_PLAYRESX.len()].to_owned() + &playresx + "\n";
1391                continue
1392            } else if line.starts_with(SCRIPT_PLAYRESY){
1393                new_lines[i] = line[..SCRIPT_PLAYRESY.len()].to_owned() + &playresy + "\n";
1394                continue;
1395            } else if line.starts_with(SCRIPT_SCALEDBORDERANDSHADOW){
1396                new_lines[i] = line[..SCRIPT_SCALEDBORDERANDSHADOW.len()].to_owned() + &scaledborderandshadow + "\n";
1397                continue;
1398            } else if line.starts_with(SCRIPT_YCBCR_MATRIX){
1399                new_lines[i] = line[..SCRIPT_YCBCR_MATRIX.len()].to_owned() + &ycbcr_matrix + "\n";
1400                continue;
1401            }
1402        }
1403
1404        for line in new_lines {
1405            total_lines.push_str(line.as_str());
1406        }
1407
1408        return total_lines;
1409    }
1410
1411    fn plug_v4(&self, v4_info: V4Format) -> String {
1412        let array = v4_info.get_array();
1413        let mut values = Vec::new();
1414        let mut v4_lines = Vec::new();
1415        let mut total_v4 = String::new();
1416        v4_lines.push("[V4+ Styles]\n".to_string());
1417        v4_lines.push("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n".to_string());
1418        v4_lines.push(V4_STYLE_HEAD.to_string());
1419
1420        for (i, value) in array.into_iter().enumerate() {
1421            let style_val = value.clone().unwrap();
1422            if i < (array.len()-1) {
1423                values.push(style_val + ",");
1424            } else {
1425                values.push(style_val);
1426            }
1427        }
1428
1429        values.push("\n".to_string());
1430
1431        v4_lines.append(&mut values); 
1432
1433        for line in v4_lines {
1434            total_v4.push_str(line.as_str());
1435        }
1436        return total_v4;
1437    }
1438
1439    fn plug_events(&self, event_info: Events) -> String {
1440        let mut lines = Vec::new();
1441        let mut total_events = String::new();
1442        let dialogues = event_info.dialogues.dialogues;
1443        lines.push(EVENTS_HEADER.to_string() + "\n");
1444        lines.push("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text".to_string() + "\n");
1445
1446        for dialogue in dialogues {
1447            let dialogue_line = dialogue.to_string();
1448            lines.push(dialogue_line);
1449        }
1450        for line in lines {
1451            total_events.push_str(line.as_str());
1452        }
1453        
1454        return total_events;
1455    }
1456
1457    fn get_each_components(&self, file_contents: String) -> Components {
1458        let lines:Vec<&str> = file_contents.split("\n").collect();
1459        let script_lines = &self.get_info(&lines, SCRIPT_HEADER);
1460        let v4_lines = &self.get_info(&lines, V4_HEADER);
1461        let events_lines = &self.get_info(&lines, EVENTS_HEADER);
1462
1463        let script = self.parse_script(script_lines.to_vec()).unwrap();
1464        let v4 = self.parse_v4(v4_lines.to_vec()).unwrap();
1465        let events = self.parse_event(events_lines.to_vec()).unwrap();
1466
1467        Components {
1468            script,
1469            v4,
1470            events,
1471        }.clone()
1472    }
1473    fn parse_script(&self, script_lines: Vec<String>) -> Option<ScriptInfo> {
1474        let mut script_type: Option<String>= None;
1475        let mut script_playerresx: Option<String>= None;
1476        let mut script_playerresy: Option<String>= None;
1477        let mut script_scaledborderandshadow: Option<String>= None;
1478        let mut script_ycbcr_matrix: Option<String>= None;
1479
1480        for line in &script_lines {
1481            if line.starts_with(SCRIPT_TYPE) {
1482                script_type = Some(line[SCRIPT_TYPE.len()..].to_owned());
1483                continue
1484            } else if line.starts_with(SCRIPT_PLAYRESX){
1485                script_playerresx= Some(line[SCRIPT_PLAYRESX.len()..].to_owned());
1486                continue
1487            } else if line.starts_with(SCRIPT_PLAYRESY){
1488                script_playerresy= Some(line[SCRIPT_PLAYRESY.len()..].to_owned());
1489                continue;
1490            } else if line.starts_with(SCRIPT_SCALEDBORDERANDSHADOW){
1491                script_scaledborderandshadow = Some(line[SCRIPT_SCALEDBORDERANDSHADOW.len()..].to_owned());
1492                continue;
1493            } else if line.starts_with(SCRIPT_YCBCR_MATRIX){
1494                script_ycbcr_matrix = Some(line[SCRIPT_YCBCR_MATRIX.len()..].to_owned());
1495                continue;
1496            }
1497        }
1498        println!("{:?}, {:?}, {:?}, {:?} {:?}", script_type, 
1499                 script_playerresx,
1500                 script_playerresy,
1501                 script_scaledborderandshadow,
1502                 script_ycbcr_matrix);
1503
1504
1505        let script_info: ScriptInfo = {
1506            let mut scriptinfo: &mut ScriptInfo = &mut ScriptInfo::new();
1507            if script_type.is_some() {
1508                scriptinfo = scriptinfo.set_scripttype(&script_type.unwrap());
1509            }
1510
1511            if script_playerresx.is_some() {
1512                scriptinfo = scriptinfo.set_playresx(&script_playerresx.unwrap());
1513            } 
1514
1515            if script_playerresy.is_some() {
1516                scriptinfo = scriptinfo.set_playresy(&script_playerresy.unwrap());
1517            }
1518
1519            if script_scaledborderandshadow.is_some() {
1520                scriptinfo = scriptinfo.set_scaledborderandshadow(&script_scaledborderandshadow.unwrap())
1521            }
1522            
1523            if script_ycbcr_matrix.is_some() {
1524                scriptinfo = scriptinfo.set_ycbcr_matrix(&script_ycbcr_matrix.unwrap())
1525            }
1526
1527            scriptinfo.clone()
1528        };
1529
1530
1531        // Error prone unwraps
1532        //let script_info = scriptinfo.
1533        //    set_scripttype(&script_type.unwrap()).
1534        //    set_playresx(&script_playerresx.unwrap()).
1535        //    set_playresy(&script_playerresy.unwrap()).
1536        //    set_scaledborderandshadow(&script_scaledborderandshadow.unwrap()).
1537        //    set_ycbcr_matrix(&script_ycbcr_matrix.unwrap()).clone();
1538
1539        Some(script_info)
1540}
1541    fn parse_event(&self, event_lines: Vec<String>) -> Option<Events>{
1542        // let mut events = Vec::new();
1543        let mut raw_dialogues = Vec::new();
1544        let mut dialogues = Vec::new();
1545        
1546        for line in event_lines {
1547            if line.starts_with(EVENT_HEAD) {
1548                raw_dialogues.push(line[EVENT_HEAD.len()..].to_string());
1549            }
1550        }
1551        for dialogue in &raw_dialogues {
1552            let splitted_dialogue: Vec<&str> = dialogue.split(',').collect();
1553            let dialogue = Dialogue::new().
1554                set_layer(splitted_dialogue[0]).
1555                set_start(splitted_dialogue[1]).
1556                set_end(splitted_dialogue[2]).
1557                set_style(splitted_dialogue[3]).
1558                set_name(splitted_dialogue[4]).
1559                set_marginl(splitted_dialogue[5]).
1560                set_marginr(splitted_dialogue[6]).
1561                set_marginv(splitted_dialogue[7]).
1562                set_effect(splitted_dialogue[8]).
1563                set_text(splitted_dialogue[9]);
1564            
1565            dialogues.push(dialogue);
1566        }
1567
1568        let dialogues = Dialogues {
1569            dialogues,
1570        };
1571
1572        return Some(Events {
1573            dialogues,
1574        })
1575
1576
1577    }
1578    fn parse_v4(&self, v4_lines: Vec<String>) -> Option<V4Format>{
1579        let mut style_line: Option::<String> = None;
1580        for line in &v4_lines {
1581            if line.starts_with(V4_STYLE_HEAD) {
1582                style_line = Some(line[V4_STYLE_HEAD.len()..].to_string());
1583                break;
1584            }
1585        }
1586        if let Some(style_data) = style_line {
1587            let values: Vec<&str> = style_data.split(',').collect();
1588            println!("{:?}", values);
1589
1590            let v4format = V4Format::new().
1591                set_name(values[0]).
1592                set_fontname(values[1]).
1593                set_fontsize(values[2]).
1594                set_primarycolour(values[3]).
1595                set_secondarycolour(values[4]).
1596                set_outlinecolour(values[5]).
1597                set_backcolour(values[6]).
1598                set_bold(values[7]).
1599                set_italic(values[8]).
1600                set_underline(values[9]).
1601                set_strikeout(values[10]).
1602                set_scalex(values[11]).
1603                set_scaley(values[12]).
1604                set_spacing(values[13]).
1605                set_angle(values[14]).
1606                set_borderstyle(values[15]).
1607                set_outline(values[16]).
1608                set_shadow(values[17]).
1609                set_alignment(values[18]).
1610                set_marginl(values[19]).
1611                set_marginr(values[20]).
1612                set_marginv(values[22]).
1613                set_encoding(values[22]).clone();
1614
1615            return Some(v4format);
1616        } else {
1617            eprintln!("Unable to parse v4!");
1618            println!("{:?}", &v4_lines);
1619            return None
1620        }
1621//["Default", "Arial", "16", "&Hffffff", "&Hffffff", "&H0", "&H0", "0", "0", "0", "0", "100", "100", "0", "0", "1", "1", "0", "2", "10", "10", "10", "1"]
1622    }
1623    fn get_info(&self, lines: &Vec<&str>, header: &str) -> Vec<String> {
1624        let mut script_lines = Vec::new();
1625        let mut found_script_header = false;
1626        for line in lines {
1627            let line = if line.ends_with('\n') {
1628                &line[..line.len()-1]
1629            } else if line.ends_with('\r'){
1630                &line[..line.len()-1]
1631            }else if line.ends_with("\r\n"){
1632                &line[..line.len()-2]
1633            }else {
1634                line
1635            };
1636            if line == header{
1637                found_script_header = true;
1638                script_lines.push(line.to_string());
1639                script_lines.push("\n".to_string());
1640                continue
1641            }
1642            if found_script_header {
1643                if line.starts_with('[') {
1644                    break;
1645                } else if line.starts_with(';') {
1646                    continue;
1647                } else {
1648                    script_lines.push(line.to_string());
1649                }
1650            } else {
1651                continue;
1652            }
1653        }
1654        return script_lines;
1655    }
1656}
1657
1658impl AssFile {
1659    /// Construct `AssFile` from an existing `.ass` file. 
1660    ///
1661    /// # Example
1662    /// ```rust
1663    /// use ass_parser::AssFile;
1664    /// let mut ass_file = ass_parser::AssFile::from_file("./examples/subtitles.ass").expect("error while reading file.");
1665    /// ```
1666    pub fn from_file(filename: &str) -> std::result::Result<AssFile, std::io::Error> {
1667        let file_contents = get_contents(&filename);
1668        let parser = Parser::new();
1669        match file_contents {
1670            Ok(mut contents) => {
1671                while !contents.starts_with("[") {
1672                    if contents.is_empty() {
1673                        return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "The given file is empty or malformed"));
1674                    }
1675                    contents.remove(0);
1676                }
1677                let components = parser.get_each_components(contents);
1678                Ok(
1679                    Self{
1680                    _ass_file: filename.to_string(),
1681                    components,
1682                })
1683            },
1684            Err(e) => {
1685                return Err(e)
1686            }
1687        }
1688    }
1689
1690}
1691
1692impl AssFile {
1693    /// save an instance of `AssFile` to an `.ass` file. 
1694    /// # Example 
1695    /// ```rust
1696    /// use hex_color::HexColor;
1697    /// use ass_parser::{AssFile, V4Format, AssFileOptions};
1698
1699    ///
1700    /// fn main() -> Result<(), std::io::Error>{
1701    ///    let mut ass_file = ass_parser::AssFile::from_file("./examples/subtitles.ass")?;
1702    ///    ass_file.components.script 
1703    ///        .set_scripttype("v4.00+")
1704    ///        .set_playresx("384")
1705    ///        .set_playresy("288")
1706    ///        .set_scaledborderandshadow("yes")
1707    ///        .set_ycbcr_matrix("None");
1708    ///
1709    ///    ass_file.components.v4.set_v4(V4Format::default());
1710    ///
1711    ///    AssFile::save_file(&ass_file, "modified_subtitles.ass");
1712    ///    Ok(())
1713    /// }
1714    /// ```
1715    pub fn save_file(file_components: &AssFile, filename: &str) {
1716        let parser = Parser::new();
1717        let components = &file_components.components;
1718
1719        let file_data = parser.combine_components(components);
1720        write_contents(filename, &file_data);
1721    }
1722}
1723
1724impl AssFileOptions {
1725}
1726
1727impl AssFileOptions{
1728    /// Get BB:GG:RR representation of colors in Hexadecimal form
1729    pub fn get_ass_color(color: HexColor) -> String {
1730        let red = color.r;
1731        let green = color.g;
1732        let blue = color.b;
1733
1734        let red_hex = format!("{:x}", red);
1735        let green_hex = format!("{:x}", green);
1736        let blue_hex = format!("{:x}", blue);
1737
1738        let reversed_hex_color = format!("{}{}{}", blue_hex, green_hex, red_hex);
1739
1740        // let mut ass_format_color = format!(r"\c&H{}&", reversed_hex_color);
1741        let ass_format_color = format!("&H{}", reversed_hex_color);
1742        // ass_format_color.push('}');
1743        // ass_format_color = "{".to_owned() + &ass_format_color;
1744
1745        return ass_format_color;
1746    }
1747
1748
1749    pub fn get_ass_color_text(color: HexColor) -> String {
1750        let red = color.r;
1751        let green = color.g;
1752        let blue = color.b;
1753
1754        let red_hex = format!("{:x}", red);
1755        let green_hex = format!("{:x}", green);
1756        let blue_hex = format!("{:x}", blue);
1757
1758        let reversed_hex_color = format!("{}{}{}", blue_hex, green_hex, red_hex);
1759
1760        let mut ass_format_color = format!(r"\c&H{}&", reversed_hex_color);
1761        ass_format_color.push('}');
1762        ass_format_color = "{".to_owned() + &ass_format_color;
1763
1764        return ass_format_color;
1765    }
1766
1767    fn _change_ass_subtitle_color(ass_file: &str, color: HexColor) -> std::result::Result<(), std::io::Error>{
1768        if !check_path_exists(ass_file){
1769            eprintln!("ERROR: File {} does not exist", ass_file);
1770            return Ok(());
1771        }
1772
1773        let mut file_data = String::new();
1774        let mut file_buffer = fs::File::open(ass_file)?;
1775        let ass_color = Self::get_ass_color(color);
1776        file_buffer.read_to_string(&mut file_data)?;
1777
1778        let lines:Vec<&str> = file_data.split("\r\n").collect();
1779        let mut subtitle_lines = Vec::new();
1780        let mut new_lines = Vec::new();
1781
1782        for line in lines {
1783            if line.starts_with("Dialogue:") {
1784                subtitle_lines.push(line);
1785            }
1786        }
1787
1788       for (_idx, line) in subtitle_lines.into_iter().enumerate() {
1789           let new_line = match line.rfind(",,") {
1790               Some(i) => {
1791                   let mut new_line = String::new();
1792                   new_line.push_str(&line[..i+2]);
1793                   new_line.push_str(&ass_color);
1794                   new_line.push_str(&line[i+2..]);
1795                   new_line.push_str("\r\n");
1796                   new_line
1797               },
1798               None => {
1799                   eprintln!("Unable to find match in line: {}", line);
1800                   line.to_string()
1801               }
1802           };
1803           new_lines.push(new_line);
1804       } 
1805       for line in &new_lines{
1806           println!("{}", line);
1807       }
1808
1809       _write_dialogues(ass_file, new_lines);
1810
1811        Ok(())
1812    }
1813
1814}
1815
1816
1817//{\c&He3cb44&}
1818
1819fn check_path_exists(path: &str) -> bool {
1820    fs::metadata(path).is_ok()
1821}
1822
1823fn _write_dialogues(filename: &str, dialogues: Vec<String>) {
1824    if !check_path_exists(filename){
1825        eprintln!("ERROR: File {} does not exist", filename);
1826        return
1827    }
1828    let mut file = fs::OpenOptions::new().read(true).write(true).open(filename).unwrap();
1829    let mut contents = String::new();
1830    file.read_to_string(&mut contents).unwrap();
1831
1832    let dialogue_idx = contents.find("Dialogue: ").unwrap();
1833
1834    file.seek(std::io::SeekFrom::Start(dialogue_idx.try_into().unwrap())).unwrap();
1835
1836    for line in dialogues {
1837        file.write(line.as_bytes()).unwrap();
1838    } 
1839}
1840
1841fn write_contents(filename: &str, contents: &str) {
1842    let mut file = fs::File::create(filename).unwrap();
1843    file.write(contents.as_bytes()).unwrap();
1844}
1845
1846fn get_contents(filename: &str) -> std::result::Result<String, std::io::Error>{
1847    if !check_path_exists(filename){
1848        return Err(std::io::ErrorKind::NotFound.into());
1849    }
1850    return fs::read_to_string(filename);
1851}
1852
1853
1854
1855
1856#[cfg(test)]
1857mod tests {
1858    use super::*;
1859
1860    #[test]
1861    fn test_file_contents() {
1862        use parser::SrtData;
1863        let file_contents = get_contents("examples/rapgod.srt").unwrap();
1864
1865        let srt_data = SrtData::new();
1866        let srt_content = srt_data.parse_srt(file_contents);
1867
1868        let test_srt_content = SrtData { 
1869            index: "0".to_string(),
1870			start: "0:00:01.50".to_string(),
1871			end: "0:00:04.90".to_string(),
1872			text: "Look, I was gonna go easy on you and not to hurt your feelings ".to_string(),
1873         };
1874
1875        assert_eq!(test_srt_content, srt_content[0]);
1876    }
1877
1878    #[test]
1879    fn test_from_file_wrong() {
1880        let result = AssFile::from_file("asdfasdf").map_err(|e| e.kind());
1881        let expected = std::result::Result::Err(std::io::ErrorKind::NotFound);
1882
1883        assert_eq!(expected, result);
1884    }
1885}