winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

PackageCatalogProgress.cpp (6308B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "PackageCatalogProgress.h"
      5 #include "AppInstallerStrings.h"
      6 #include "Microsoft/PredefinedInstalledSourceFactory.h"
      7 
      8 using namespace AppInstaller;
      9 using namespace AppInstaller::Repository;
     10 
     11 namespace winrt::Microsoft::Management::Deployment
     12 {
     13     namespace ProgressSinkFactory
     14     {
     15         std::shared_ptr<AppInstaller::IProgressSink> CreatePackageCatalogProgressSink(std::string sourceType, std::function<void(double)> progressReporter, bool removeOperation)
     16         {
     17             if (sourceType.empty()
     18                 || Utility::CaseInsensitiveEquals( Repository::Microsoft::PredefinedInstalledSourceFactory::Type(), sourceType))
     19             {
     20                 std::vector<std::pair<AppInstaller::ProgressType, double>> progressWeights;
     21 
     22                 // There is no download operation for remove operation, so use only percentage based progress to account for uninstall.
     23                 if (removeOperation)
     24                 {
     25                     // it is percentage based progress.
     26                     progressWeights.push_back(std::make_pair(AppInstaller::ProgressType::Percent, 1.0));
     27                 }
     28                 else
     29                 {
     30                     // Add/Update operation has two progress types:
     31                     // 1. Bytes for downloading index and
     32                     // 2. Percent for index installation.
     33                     progressWeights.push_back(std::make_pair(AppInstaller::ProgressType::Bytes, 0.7));
     34                     progressWeights.push_back(std::make_pair(AppInstaller::ProgressType::Percent, 0.3));
     35                 }
     36 
     37                 return std::make_shared<PreIndexedPackageCatalogProgressSink>(progressWeights, progressReporter);
     38             }
     39             else
     40             {
     41                 return std::make_shared<CompletionOnlyProgressSink>(progressReporter);
     42             }
     43         }
     44     }
     45 
     46     CompletionOnlyProgressSink::CompletionOnlyProgressSink(std::function<void(double)> progressReporter) :
     47         m_progressReporter(progressReporter)
     48     {
     49         if (!m_progressReporter)
     50         {
     51             THROW_HR(E_INVALIDARG);
     52         }
     53     }
     54 
     55     void CompletionOnlyProgressSink::OnProgress(uint64_t /*current*/, uint64_t /*maximum*/, AppInstaller::ProgressType /*type*/)
     56     {
     57     }
     58 
     59     void CompletionOnlyProgressSink::SetProgressMessage(std::string_view /*message*/)
     60     {
     61     }
     62 
     63     void CompletionOnlyProgressSink::BeginProgress()
     64     {
     65         m_progressReporter(0);
     66     }
     67 
     68     void CompletionOnlyProgressSink::EndProgress(bool /*hideProgressWhenDone*/)
     69     {
     70         m_progressReporter(100);
     71     }
     72 
     73     PreIndexedPackageCatalogProgressSink::PreIndexedPackageCatalogProgressSink(std::vector<std::pair<AppInstaller::ProgressType, double>> progressWeights, std::function<void(double)> progressReporter) :
     74         m_progressWeights(progressWeights), m_progressReporter(progressReporter)
     75     {
     76         if (!m_progressReporter)
     77         {
     78             THROW_HR(E_INVALIDARG);
     79         }
     80 
     81         // If no weights are provided, default to percent.
     82         if (m_progressWeights.empty())
     83         {
     84             m_progressWeights.push_back(std::make_pair(AppInstaller::ProgressType::Percent, 1.0));
     85         }
     86 
     87         // Calculate the total weight.
     88         double totalWeight = 0;
     89         for (const auto& weight : m_progressWeights)
     90         {
     91             if (weight.first != AppInstaller::ProgressType::None)
     92             {
     93                 totalWeight += weight.second;
     94             }
     95         }
     96 
     97         // If the total weight is greater than 1, throw an exception.
     98         if (totalWeight != 1.0)
     99         {
    100             THROW_HR(E_INVALIDARG);
    101         }
    102     }
    103 
    104     void PreIndexedPackageCatalogProgressSink::OnProgress(uint64_t current, uint64_t maximum, AppInstaller::ProgressType type)
    105     {
    106         if (maximum == 0 || type == AppInstaller::ProgressType::None)
    107         {
    108             return;
    109         }
    110 
    111         double progress = static_cast<double>(current) / maximum;
    112         m_progressValues[type] = progress;
    113 
    114         double totalProgress = 0.0;
    115         double totalWeight = 0.0;
    116 
    117         // Calculate the total progress.
    118         for (const auto& [progressType, weight] : m_progressWeights)
    119         {
    120             double progressValue = m_progressValues[progressType];
    121 
    122             // [NOTE:] Sequential execution assumption & Handling incomplete progress reports :
    123             // This progress calculation assumes that each operation is executed sequentially, meaning the download must be complete before
    124             // the installation begins.If the download fails, the installation will not proceed.However, there may be cases where the previous
    125             // operation completes successfully, but its onprogress callback does not report 100% completion(e.g., the last progress report for
    126             // the download was at 90%, but the download is complete, and the installation has started).This can result in the total progress not
    127             // reaching 100% after the last operation completes due to the gap in the previous operation's progress report.To handle this, consider
    128             // the progress for the last operation as complete by assigning its full weight while computing progress for the following operation.
    129             // For example, while computing progress for the installation, consider the download operation complete even if it did not report progress
    130             // exactly at 100%.
    131             if (progressValue != 0)
    132             {
    133                 totalProgress = totalWeight;
    134             }
    135 
    136             // Adjust the total progress value based on the weight.
    137             totalWeight += weight;
    138             totalProgress += progressValue * weight;
    139         }
    140 
    141         m_progressReporter(totalProgress * 100);
    142     }
    143 
    144     void PreIndexedPackageCatalogProgressSink::SetProgressMessage(std::string_view /*message*/)
    145     {
    146     }
    147 
    148     void PreIndexedPackageCatalogProgressSink::BeginProgress()
    149     {
    150         m_progressReporter(0);
    151     }
    152 
    153     void PreIndexedPackageCatalogProgressSink::EndProgress(bool /*hideProgressWhenDone*/)
    154     {
    155         m_progressReporter(100);
    156     }
    157 }