# frozen_string_literal: true module FeatherCaseWizard class CLI def initialize(ui: HighLine.new, cases_root: CASES_ROOT, model_source: MODEL_SOURCE, renderer_source: RENDERER_SOURCE, toolchain: Toolchain.new) @ui = ui @cases_root = Pathname(cases_root) @model_source = Pathname(model_source) @renderer_source = Pathname(renderer_source) @toolchain = toolchain @wizard = Wizard.new(ui: ui) end def run @ui.say(@ui.color("Feather Case Wizard", :bold)) loop do action = @ui.choose("\nWhat would you like to do?") do |menu| menu.choice("Create a new case") { :create } menu.choice("Edit an existing case") { :edit } menu.choice("Rebuild an existing case") { :rebuild } menu.choice("Exit") { :exit } end return 0 if action == :exit case action when :create then create_case when :edit then edit_case when :rebuild then rebuild_case end rescue ConfigError, ToolError, Errno::EACCES => e @ui.say(@ui.color("\n#{e.message}", :red)) rescue Interrupt @ui.say("\nCancelled.") return 130 end end private def create_case config = @wizard.create destination = config.package_dir(@cases_root) if destination.exist? && !@ui.agree("#{destination} already exists. Replace it? (y/n) ") { |q| q.default = "no" } return end build(config, @model_source, @renderer_source) if @wizard.confirm_build(config) end def edit_case path = choose_case return unless path config = Config.load(path) package = path.dirname source = package.join("source/feather_case.scad") renderer = package.join("source/render_stl_previews.py") source, renderer = choose_generator(config, source, renderer) config = @wizard.edit(config) build(config, source, renderer) if @wizard.confirm_build(config) end def rebuild_case path = choose_case return unless path config = Config.load(path) return unless @wizard.confirm_build(config) package = path.dirname build(config, package.join("source/feather_case.scad"), package.join("source/render_stl_previews.py")) end def choose_case paths = Dir.glob(@cases_root.join("*/case.yml")).sort.map { |item| Pathname(item) } if paths.empty? @ui.say("No saved cases found under #{@cases_root}.") return nil end @ui.choose("Choose a saved case") do |menu| paths.each do |path| label = Config.load(path).name menu.choice("#{label} (#{path.dirname.basename})") { path } rescue ConfigError menu.choice("Invalid configuration (#{path.dirname.basename})") { path } end end end def choose_generator(config, snapshot, renderer) return [@model_source, @renderer_source] unless snapshot.file? snapshot_sha = Digest::SHA256.file(snapshot).hexdigest current_sha = Digest::SHA256.file(@model_source).hexdigest return [snapshot, renderer] if snapshot_sha == current_sha @ui.say("This case uses a different generator snapshot.") upgrade = @ui.agree("Upgrade it to the current generator? (y/n) ") do |question| question.default = "no" end upgrade ? [@model_source, @renderer_source] : [snapshot, renderer] end def build(config, source, renderer) destination = PackageBuilder.new( config, root: @cases_root, toolchain: @toolchain, model_source: source, renderer_source: renderer, reporter: ->(message) { @ui.say(" #{message}…") } ).build! @ui.say(@ui.color("\nGenerated #{destination}", :green)) end end end