-- This source is licensed under the Sunset License v1.0
with
Ada.Characters.Latin_1,
Ada.Characters.Handling,
Ada.Exceptions,
GNAT.Command_Line,
GNAT.Strings,
Ada.Command_Line,
Ada.Directories,
Ada.Strings.Unbounded,
Ada.Text_IO,
Datatypes,
Deck_IO;
use
Ada.Text_IO,
Datatypes;
procedure Deck_Convert is
package Latin renames Ada.Characters.Latin_1;
package Charhand renames Ada.Characters.Handling;
package ACom renames Ada.Command_Line;
package GCom renames GNAT.Command_Line;
package GStr renames GNAT.Strings;
package FD renames Ada.Directories;
package SU renames Ada.Strings.Unbounded;
use type FD.File_Kind;
use type SU.Unbounded_String;
function "+"
(S : in String)
return SU.Unbounded_String
renames SU.To_Unbounded_String;
function "-"
(US : in SU.Unbounded_String)
return String
renames SU.To_String;
procedure Strip_Formatting
(Notes : in out Note_Vector)
is
procedure Strip
(Text : in out Note;
Item : in String)
is
Position : Natural;
begin
for F of Text.Fields loop
loop
Position := SU.Index
(Source => SU.Unbounded_String (F),
Pattern => Item,
Going => Ada.Strings.Forward,
Mapping => Charhand.To_Lower'Access);
exit when Position = 0;
SU.Delete (SU.Unbounded_String (F), Position, Position + Item'Length - 1);
end loop;
end loop;
end Strip;
Formatting : array (Positive range <>) of SU.Unbounded_String :=
(+"", +"", +"", +"", +"", +"", +"", +"",
+"", +"", +"", +"", +"", +"",
+"", +"", +"", +"", +"", +"");
begin
for N of Notes loop
for S of Formatting loop
Strip (N, -S);
end loop;
end loop;
end Strip_Formatting;
Config : GCom.Command_Line_Configuration;
Further_Help : String := "Try ""deckconv --help"" for more information.";
Verbose : aliased Boolean;
Help : aliased Boolean;
Overwrite : aliased Boolean;
Strip_Form : aliased Boolean;
Format_Arg : aliased GStr.String_Access;
Input_Arg : aliased GStr.String_Access;
Output_Arg : aliased GStr.String_Access;
Deck_Format : SU.Unbounded_String;
Input_Name : SU.Unbounded_String;
Output_Name : SU.Unbounded_String;
Deck : Deck_IO.Deck_Handle;
Models : Model_Map;
Notes : Note_Vector;
Media : Media_Collection;
begin
GCom.Define_Switch
(Config => Config, Output => Verbose'Access,
Switch => "-v", Long_Switch => "--verbose",
Help => "chatty output on stderr");
GCom.Define_Switch
(Config => Config, Output => Help'Access,
Switch => "-h", Long_Switch => "--help",
Help => "show this help information");
GCom.Define_Switch
(Config => Config, Output => Overwrite'Access,
Switch => "-f", Long_Switch => "--force",
Help => "overwrite selected output file if present");
GCom.Define_Switch
(Config => Config, Output => Format_Arg'Access,
Switch => "-t:", Long_Switch => "--type=",
Help => "format of output data, options are CSV or FMD, default is FMD");
GCom.Define_Switch
(Config => Config, Output => Input_Arg'Access,
Switch => "-i:", Long_Switch => "--input=",
Help => "file name of input deck");
GCom.Define_Switch
(Config => Config, Output => Output_Arg'Access,
Switch => "-o:", Long_Switch => "--output=",
Help => "base name to store output data");
GCom.Define_Switch
(Config => Config, Output => Strip_Form'Access,
Switch => "-b", Long_Switch => "--bare",
Help => "strip formatting from deck notes");
GCom.Set_Usage
(Config => Config,
Usage => "[switches]",
Help =>
"Utility to convert Anki flashcard decks to Fresh Memory dictionaries." & Latin.LF &
"At minimum input and output options are required." & Latin.LF);
begin
GCom.Getopt (Config);
exception
when GCom.Exit_From_Command_Line =>
ACom.Set_Exit_Status (ACom.Failure);
return;
when GCom.Invalid_Switch =>
ACom.Set_Exit_Status (ACom.Failure);
return;
end;
if Format_Arg.all = "" then
if Verbose then
Put_Line (Standard_Error, "WARNING: No output deck format selected. " &
"Proceeding with FMD as default." & Latin.LF);
end if;
Deck_Format := +"fmd";
else
Deck_Format := +(Charhand.To_Lower (Format_Arg.all));
end if;
if Deck_Format /= "csv" and Deck_Format /= "fmd" then
Put_Line (Standard_Error,
"ERROR: Output deck format required. Valid options are CSV or FMD." &
Latin.LF & Further_Help);
ACom.Set_Exit_Status (ACom.Failure);
return;
end if;
if Input_Arg.all = "" then
Put_Line (Standard_Error, "ERROR: File name of input deck was not provided." &
Latin.LF & Further_Help);
ACom.Set_Exit_Status (ACom.Failure);
return;
end if;
Input_Name := +(Input_Arg.all);
if not FD.Exists (-Input_Name) then
Put_Line (Standard_Error, "ERROR: Input deck does not exist." &
Latin.LF & Further_Help);
ACom.Set_Exit_Status (ACom.Failure);
return;
end if;
if Output_Arg.all = "" then
Put_Line (Standard_Error, "ERROR: Base file name for output deck was not provided." &
Latin.LF & Further_Help);
ACom.Set_Exit_Status (ACom.Failure);
return;
end if;
Output_Name := +(Output_Arg.all);
if FD.Exists ((-Output_Name) & "." & (-Deck_Format)) and not Overwrite then
Put_Line (Standard_Error, "ERROR: Output deck file name already exists." &
Latin.LF & Further_Help);
ACom.Set_Exit_Status (ACom.Failure);
return;
end if;
-- Program functionality proper starts here
if Verbose then
Put (Standard_Error, "Opening input deck database...");
end if;
Deck_IO.Open_Database (-Input_Name, Deck);
if Verbose then
Put_Line (Standard_Error, " Done");
Put (Standard_Error, "Querying models...");
end if;
Deck_IO.Query_Models (Deck, Models);
if Verbose then
Put_Line (Standard_Error, " Done");
Put (Standard_Error, "Querying notes...");
end if;
Deck_IO.Query_Notes (Deck, Notes);
if Verbose then
Put_Line (Standard_Error, " Done");
Put (Standard_Error, "Closing database...");
end if;
Deck_IO.Close_Database (Deck);
if Verbose then
Put_Line (Standard_Error, " Done");
New_Line (Standard_Error);
end if;
if Strip_Form then
if Verbose then
Put (Standard_Error, "Stripping formatting from deck notes...");
end if;
Strip_Formatting (Notes);
if Verbose then
Put_Line (Standard_Error, " Done");
New_Line (Standard_Error);
end if;
end if;
declare
Contain_Dir : String := FD.Containing_Directory (-Output_Name);
Simple_Name : String := FD.Simple_Name (-Output_Name);
begin
if Deck_Format = "csv" then
if Verbose then
Put (Standard_Error, "Writing output to CSV...");
end if;
Deck_IO.Write_CSV (Contain_Dir, Simple_Name, Models, Notes, Overwrite);
if Verbose then
Put_Line (Standard_Error, " Done");
end if;
elsif Deck_Format = "fmd" then
if Verbose then
Put (Standard_Error, "Reading media...");
end if;
Deck_IO.Read_Media_Collection (-Input_Name, Media);
if Verbose then
Put_Line (Standard_Error, " Done");
Put (Standard_Error, "Writing output to Fresh Memory Dictionary...");
end if;
Deck_IO.Write_FMD (Contain_Dir, Simple_Name, Models, Notes, Media, Overwrite);
if Verbose then
Put_Line (Standard_Error, " Done");
end if;
else
raise Constraint_Error;
end if;
end;
exception
when Fail : FD.Name_Error =>
Put_Line (Standard_Error, Latin.LF & "ERROR: " & Ada.Exceptions.Exception_Message (Fail) &
Latin.LF & Further_Help);
ACom.Set_Exit_Status (ACom.Failure);
end Deck_Convert;