File: rustfile.cxx

package info (click to toggle)
libreoffice 4%3A26.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,833,120 kB
  • sloc: cpp: 4,395,780; xml: 499,109; java: 254,438; python: 81,820; ansic: 33,823; perl: 30,297; javascript: 19,722; sh: 12,050; makefile: 10,854; cs: 8,865; yacc: 8,549; objc: 2,131; lex: 1,385; asm: 1,231; awk: 996; pascal: 914; csh: 20; sed: 5
file content (664 lines) | stat: -rw-r--r-- 20,573 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

// Implementation of Rust file generation and module management

#include "rustfile.hxx"
#include <algorithm>
#include <filesystem>
#include <iostream>
#include <vector>
#include <functional>
#include <codemaker/global.hxx>

RustFile::RustFile(std::string_view directory, std::string_view typeName)
    : m_filePath(createFilePath(directory, typeName))
    , m_indentLevel(0)
{
}

std::string RustFile::getPath() const { return m_filePath.string(); }

void RustFile::openFile()
{
    std::filesystem::create_directories(m_filePath.parent_path());
    createModFiles();
    addFileToModDeclaration();
    m_fileStream.open(m_filePath, std::fstream::out | std::fstream::trunc);
    m_indentLevel = 0;
}

void RustFile::closeFile() { m_fileStream.close(); }

void RustFile::createLibFile(const std::filesystem::path& outputDir)
{
    // Create generated/rustmaker directory first, then src directory inside it
    std::filesystem::path generatedDir = outputDir / "generated";
    std::filesystem::path rustmakerDir = generatedDir / "rustmaker";
    std::filesystem::path srcDir = rustmakerDir / "src";
    std::filesystem::create_directories(srcDir);

    std::filesystem::path libFile = srcDir / "lib.rs";
    if (!std::filesystem::exists(libFile))
    {
        std::ofstream libStream(libFile);
        libStream << "//! Generated Rust bindings for LibreOffice UNO types\n";
        libStream << "//! \n";
        libStream << "//! This crate contains automatically generated Rust bindings\n";
        libStream << "//! for LibreOffice UNO (Universal Network Objects) types.\n\n";
        libStream.close();
    }
}

void RustFile::createCargoFile(const std::filesystem::path& outputDir, const std::string& crateName)
{
    // Create Cargo.toml in the generated/rustmaker directory
    std::filesystem::path generatedDir = outputDir / "generated";
    std::filesystem::path rustmakerDir = generatedDir / "rustmaker";
    std::filesystem::path cargoFile = rustmakerDir / "Cargo.toml";
    if (!std::filesystem::exists(cargoFile))
    {
        std::ofstream cargoStream(cargoFile);
        cargoStream << "[package]\n";
        cargoStream << "name = \"" << crateName << "\"\n";
        cargoStream << "version = \"0.1.0\"\n";
        cargoStream << "edition = \"2024\"\n";
        cargoStream << "\n";
        cargoStream << "[lib]\n";
        cargoStream << "name = \"rustmaker\"\n";
        // Support both dynamic and static linking
        cargoStream << "crate-type = [\"cdylib\", \"rlib\"]\n";
        cargoStream << "description = \"Generated Rust bindings for LibreOffice UNO types\"\n\n";
        cargoStream << "[dependencies]\n";
        cargoStream << "# UNO runtime dependencies\n";
        cargoStream << "rust_uno = { path = \"../../../../rust_uno\" }\n";
        cargoStream.close();
    }
}

void RustFile::createGeneratedModFile(const std::filesystem::path& outputDir)
{
    // Create generated directory and its mod.rs file
    std::filesystem::path generatedDir = outputDir / "generated";
    std::filesystem::create_directories(generatedDir);

    std::filesystem::path modFile = generatedDir / "mod.rs";
    if (!std::filesystem::exists(modFile))
    {
        std::ofstream modStream(modFile);
        modStream << "//! Generated opaque bindings for LibreOffice UNO types\n";
        modStream << "//!\n";
        modStream << "//! This module contains both Rust and C++ opaque bindings\n";
        modStream << "//! auto-generated by rustmaker\n\n";
        modStream << "// Include the generated rustmaker bindings directly\n";
        modStream << "// The rustmaker directory contains a complete Rust project structure\n";
        modStream << "// We include its lib.rs as a module here\n";
        modStream << "#[path = \"rustmaker/src/lib.rs\"]\n";
        modStream << "pub mod rustmaker;\n\n";
        modStream << "// Re-export the UNO namespace for easier access\n";
        modStream << "pub use rustmaker::com;\n\n";
        modStream << "// Note: cpp_rustmaker directory contains C++ bridge files\n";
        modStream << "// These are compiled separately and linked via build.rs\n";
        modStream.close();
    }
}

void RustFile::finalizeModFiles(const std::filesystem::path& outputDir)
{
    // Find all directories in the rustmaker source tree and update their mod.rs files
    std::filesystem::path rustmakerSrc = outputDir / "generated" / "rustmaker" / "src";

    if (!std::filesystem::exists(rustmakerSrc))
    {
        return;
    }

    // Start processing from the rustmaker src directory
    processDirectoryRecursively(rustmakerSrc);
}

RustFile& RustFile::beginBlock()
{
    beginLine();
    append("{");
    endLine();
    ++m_indentLevel;
    return *this;
}

RustFile& RustFile::endBlock()
{
    --m_indentLevel;
    beginLine();
    append("}");
    endLine();
    return *this;
}

// Text output methods
RustFile& RustFile::beginLine()
{
    for (int i = 0; i < m_indentLevel; i++)
    {
        m_fileStream << "    ";
    }
    return *this;
}

RustFile& RustFile::extraIndent()
{
    m_fileStream << "    ";
    return *this;
}

RustFile& RustFile::append(std::string_view item)
{
    m_fileStream << item;
    return *this;
}

RustFile& RustFile::append(std::u16string_view item)
{
    m_fileStream << u2b(item);
    return *this;
}

RustFile& RustFile::endLine()
{
    m_fileStream << '\n';
    return *this;
}

void RustFile::createModFiles()
{
    std::filesystem::path outputRoot = findOutputRoot();

    // Create lib and cargo files only once at the proper root level
    // outputRoot should be the rustmaker directory, so we pass its parent to the create functions
    // which will then create the rustmaker directory and put files inside it
    createGeneratedModFile(
        outputRoot.parent_path().parent_path()); // Create generated/mod.rs at top level
    createLibFile(
        outputRoot.parent_path().parent_path()); // Create in the directory containing generated/
    createCargoFile(outputRoot.parent_path().parent_path(), "rustmaker");

    // Build path hierarchy from file location up to project root
    std::filesystem::path workingPath = m_filePath.parent_path();
    std::vector<std::filesystem::path> pathsToProcess;

    // Collect all directory paths from current file up to root
    while (workingPath != outputRoot && workingPath.has_parent_path())
    {
        pathsToProcess.push_back(workingPath);
        workingPath = workingPath.parent_path();
    }

    // Process paths from root down to ensure proper mod.rs structure
    std::reverse(pathsToProcess.begin(), pathsToProcess.end());
    for (const auto& path : pathsToProcess)
    {
        ensureModFileExists(path);
    }
}

// Complex path resolution - walks up directory tree to find project root
std::filesystem::path RustFile::findOutputRoot()
{
    std::filesystem::path currentPath = m_filePath.parent_path();

    // Walk up until we find a directory structure indicating project root
    while (currentPath.has_parent_path() && currentPath != currentPath.parent_path())
    {
        if (currentPath.filename() == "src")
        {
            return currentPath.parent_path(); // Return rustmaker directory
        }

        // Look for rustmaker directory as the project root
        if (currentPath.filename() == "rustmaker")
        {
            return currentPath;
        }

        // Look for existing src/ subdirectory as root indicator
        std::filesystem::path srcDir = currentPath / "src";
        if (std::filesystem::exists(srcDir))
        {
            return currentPath;
        }

        currentPath = currentPath.parent_path();
    }

    // Fallback: find parent of src directory (which should be rustmaker)
    std::filesystem::path result = m_filePath.parent_path();
    while (result.has_parent_path() && result.filename() != "src")
    {
        result = result.parent_path();
    }

    if (result.filename() == "src")
    {
        return result.parent_path(); // Return rustmaker directory
    }

    return result; // Default case
}

void RustFile::addFileToModDeclaration()
{
    std::filesystem::path parentDir = m_filePath.parent_path();
    std::filesystem::path modFile = parentDir / "mod.rs";
    std::string fileName = m_filePath.stem().string(); // Get filename without extension

    if (std::filesystem::exists(modFile))
    {
        addModuleDeclaration(modFile, fileName);
    }
}

void RustFile::ensureModFileExists(const std::filesystem::path& dirPath)
{
    // Skip mod.rs creation for the src directory itself
    if (dirPath.filename() == "src")
    {
        return;
    }

    std::filesystem::path modFile = dirPath / "mod.rs";
    if (!std::filesystem::exists(modFile))
    {
        // Create mod.rs file with proper structure
        std::ofstream modStream(modFile);
        modStream << "// Generated mod.rs file\n\n";
        modStream << "#[allow(non_snake_case)]\n";
        modStream.close();
    }

    // After ensuring the mod.rs exists, collect all subdirectories and modules
    // to automatically add pub use ::* patterns
    std::vector<std::string> childModules;

    // Scan directory for subdirectories (modules) and .rs files
    if (std::filesystem::exists(dirPath))
    {
        try
        {
            for (const auto& entry : std::filesystem::directory_iterator(dirPath))
            {
                if (entry.is_directory())
                {
                    std::string dirName = entry.path().filename().string();
                    childModules.push_back(dirName);
                }
                else if (entry.is_regular_file() && entry.path().extension() == ".rs")
                {
                    std::string fileName = entry.path().stem().string();
                    if (fileName != "mod") // Don't include mod.rs itself
                    {
                        childModules.push_back(fileName);
                    }
                }
            }
        }
        catch (const std::filesystem::filesystem_error&)
        {
            // Handle permission errors gracefully
        }
    }

    // Add module declarations and pub use statements for all child modules
    for (const std::string& moduleName : childModules)
    {
        addModuleDeclaration(modFile, moduleName);
    }

    // Recursively ensure parent modules are properly declared
    if (dirPath.has_parent_path())
    {
        std::filesystem::path parentDir = dirPath.parent_path();
        std::filesystem::path outputRoot = findOutputRoot();

        if (parentDir >= outputRoot)
        {
            std::string moduleName = dirPath.filename().string();

            if (parentDir.filename() == "src")
            {
                std::filesystem::path libFile = parentDir / "lib.rs";
                if (std::filesystem::exists(libFile))
                {
                    addModuleDeclaration(libFile, moduleName);
                }
            }
            else if (parentDir == outputRoot)
            {
                std::filesystem::path libFile = parentDir / "src" / "lib.rs";
                if (std::filesystem::exists(libFile))
                {
                    addModuleDeclaration(libFile, moduleName);
                }
            }
            else
            {
                ensureModFileExists(parentDir);
                std::filesystem::path parentModFile = parentDir / "mod.rs";
                if (std::filesystem::exists(parentModFile))
                {
                    addModuleDeclaration(parentModFile, moduleName);
                }
            }
        }
    }
}

void RustFile::addModuleDeclaration(const std::filesystem::path& modFile,
                                    const std::string& moduleName)
{
    // Read existing content to check for duplicates and organize sections
    std::ifstream inFile(modFile);
    std::string line;
    std::vector<std::string> pubModLines;
    std::vector<std::string> otherLines;
    bool foundModDeclaration = false;

    while (std::getline(inFile, line))
    {
        if (line.find("pub mod " + moduleName + ";") != std::string::npos)
        {
            foundModDeclaration = true;
        }

        if (line.find("pub mod ") == 0)
        {
            pubModLines.push_back(line);
        }
        else if (!(line.find("pub use ") == 0 && line.find("::*;") != std::string::npos))
        {
            // Skip old pub use re-export lines, include everything else
            otherLines.push_back(line);
        }
    }
    inFile.close();

    // If module declaration is already declared, return
    if (foundModDeclaration)
    {
        return;
    }

    // Add new declarations if not found
    if (!foundModDeclaration)
    {
        pubModLines.push_back("pub mod " + moduleName + ";");
    }
    // Re-export generation removed - causes unused import warnings

    // Rewrite the file with organized structure
    std::ofstream outFile(modFile, std::ios::trunc);

    // Write other lines first (comments, etc.)
    for (const auto& otherLine : otherLines)
    {
        // Skip re-export comment lines
        if (otherLine.find("// Re-export all items from child modules") == std::string::npos)
        {
            outFile << otherLine << "\n";
        }
    }

    // Write all pub mod declarations
    for (const auto& modLine : pubModLines)
    {
        outFile << modLine << "\n";
    }

    // Re-export section removed - causes unused import warnings

    outFile.close();
}

// Converts UNO type name to file path (dots become path separators)
std::filesystem::path RustFile::createFilePath(std::string_view dir, std::string_view type)
{
    std::string subdir(type);
    for (char& c : subdir)
        if (c == '.')
            c = '/';

    std::filesystem::path path(dir);
    path /= "rustmaker"; // Add rustmaker subdirectory for generated types
    path /= "src";
    path /= subdir + ".rs";
    return path;
}

// CppFile implementation
CppFile::CppFile(std::string_view directory, std::string_view typeName)
    : m_filePath(createCppFilePath(directory, typeName))
    , m_indentLevel(0)
{
}

CppFile::CppFile(std::string_view directory, std::string_view typeName, std::string_view extension)
    : m_filePath(createCppFilePath(directory, typeName, extension))
    , m_indentLevel(0)
{
}

std::string CppFile::getPathString() const { return m_filePath.string(); }
std::filesystem::path CppFile::getPath() const { return m_filePath; }
std::string CppFile::getExtension() const { return m_filePath.extension().string(); }

void CppFile::openFile()
{
    std::filesystem::create_directories(m_filePath.parent_path());
    m_fileStream.open(m_filePath, std::fstream::out | std::fstream::trunc);
    m_indentLevel = 0;
}

void CppFile::openFileAppend()
{
    std::filesystem::create_directories(m_filePath.parent_path());
    m_fileStream.open(m_filePath, std::fstream::out | std::fstream::app);
    m_indentLevel = 0;
}

void CppFile::closeFile() { m_fileStream.close(); }

CppFile& CppFile::beginBlock()
{
    beginLine();
    append("{");
    endLine();
    ++m_indentLevel;
    return *this;
}

CppFile& CppFile::endBlock()
{
    --m_indentLevel;
    beginLine();
    append("}");
    endLine();
    return *this;
}

CppFile& CppFile::beginLine()
{
    m_fileStream << std::string(4 * m_indentLevel, ' ');
    return *this;
}

CppFile& CppFile::extraIndent()
{
    m_fileStream << "    ";
    return *this;
}

CppFile& CppFile::append(std::string_view item)
{
    m_fileStream << item;
    return *this;
}

CppFile& CppFile::append(std::u16string_view item)
{
    m_fileStream << u2b(item);
    return *this;
}

CppFile& CppFile::endLine()
{
    m_fileStream << std::endl;
    return *this;
}

CppFile& CppFile::writeIncludes(const std::vector<std::string>& includes)
{
    for (const auto& include : includes)
    {
        beginLine().append("#include ").append(include).endLine();
    }
    endLine();
    return *this;
}

CppFile& CppFile::writeNamespaceBegin(const std::vector<std::string>& namespaces)
{
    for (const auto& ns : namespaces)
    {
        beginLine().append("namespace ").append(ns).append(" {").endLine();
    }
    endLine();
    return *this;
}

CppFile& CppFile::writeNamespaceEnd(const std::vector<std::string>& namespaces)
{
    endLine();
    for (auto it = namespaces.rbegin(); it != namespaces.rend(); ++it)
    {
        beginLine().append("} // namespace ").append(*it).endLine();
    }
    return *this;
}

std::filesystem::path CppFile::createCppFilePath(std::string_view dir, std::string_view type,
                                                 std::string_view extension)
{
    std::filesystem::path outputDir(dir);

    // For the special case of "rust_uno_bindings", create it directly in the output directory
    if (type == "rust_uno_bindings")
    {
        return outputDir / (std::string(type) + std::string(extension));
    }

    std::filesystem::path basePath = outputDir;

    std::string typeName(type);

    // Extract interface name (part after last dot)
    size_t lastDot = typeName.find_last_of('.');
    std::string interfaceName
        = (lastDot != std::string::npos) ? typeName.substr(lastDot + 1) : typeName;

    // Create namespace directory path from the full type name
    if (lastDot != std::string::npos)
    {
        // Convert namespace dots to path separators: com.sun.star.frame -> com/sun/star/frame/
        std::string namespacePath = typeName.substr(0, lastDot);

        // Replace dots with path separators
        std::replace(namespacePath.begin(), namespacePath.end(), '.', '/');

        // Build full path: cpp_rustmaker/com/sun/star/frame/XComponentLoader.hxx
        return basePath / namespacePath / (interfaceName + std::string(extension));
    }
    else
    {
        // No namespace, put directly in cpp_rustmaker directory
        return basePath / (interfaceName + std::string(extension));
    }
}

void RustFile::processDirectoryRecursively(const std::filesystem::path& currentDir)
{
    if (!std::filesystem::exists(currentDir) || !std::filesystem::is_directory(currentDir))
    {
        return;
    }

    // Skip the root src directory
    if (currentDir.filename() == "src" && currentDir.parent_path().filename() == "rustmaker")
    {
        // Process subdirectories but don't create mod.rs for src itself
        for (const auto& entry : std::filesystem::directory_iterator(currentDir))
        {
            if (entry.is_directory())
            {
                processDirectoryRecursively(entry.path());
            }
        }
        return;
    }

    std::vector<std::string> childModules;

    try
    {
        // Collect all subdirectories and .rs files
        for (const auto& entry : std::filesystem::directory_iterator(currentDir))
        {
            if (entry.is_directory())
            {
                std::string dirName = entry.path().filename().string();
                childModules.push_back(dirName);
                // Recursively process subdirectory
                processDirectoryRecursively(entry.path());
            }
            else if (entry.is_regular_file() && entry.path().extension() == ".rs")
            {
                std::string fileName = entry.path().stem().string();
                if (fileName != "mod" && fileName != "lib") // Don't include mod.rs or lib.rs
                {
                    childModules.push_back(fileName);
                }
            }
        }
    }
    catch (const std::filesystem::filesystem_error&)
    {
        // Handle permission errors gracefully
        return;
    }

    // Create/update mod.rs file if there are child modules
    if (!childModules.empty())
    {
        std::filesystem::path modFile = currentDir / "mod.rs";

        // Create the mod.rs file with all declarations
        std::ofstream modStream(modFile, std::ios::trunc);
        modStream << "// Generated mod.rs file\n\n";
        modStream << "#[allow(non_snake_case)]\n";

        for (const std::string& moduleName : childModules)
        {
            modStream << "pub mod " << moduleName << ";\n";
        }

        // Re-export section removed per request

        modStream.close();
    }
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */