1use std::slice;
6
7use nifty_download::{DownloadId, Report, Written, follow_id};
8use nifty_runtime::{DownloadCommand, DownloadField, Failure};
9
10use crate::abi::borrow_context;
11use crate::context::{AppError, Context};
12use crate::place::{Location, PlaceDesc, StateOf};
13use crate::value::{self, INT, STRING, Slot};
14
15#[derive(Debug)]
17#[repr(C)]
18pub struct DownloadBinding {
19 pub field: DownloadField,
20 pub place: *const PlaceDesc,
21}
22
23#[derive(Debug)]
26#[repr(C)]
27pub struct DownloadDesc {
28 pub url: Slot,
29 pub folder: Slot,
30 pub name: Slot,
31 pub connections: i64,
32 pub headers: Slot,
33 pub retries: i64,
34 pub bindings: *const DownloadBinding,
35 pub len: usize,
36}
37
38#[derive(Debug)]
41pub(crate) struct Targets {
42 id: DownloadId,
43 bindings: Vec<(DownloadField, Location)>,
44 span: u32,
45}
46
47pub(crate) type Request = nifty_download::Request<Targets>;
48
49impl Context {
50 pub(crate) fn report_download(
52 &self,
53 targets: &Targets,
54 report: &Report,
55 ) -> Result<(), AppError> {
56 self.write_report(targets, report)
57 .map_err(|failure| self.error(&failure, targets.span))
58 }
59
60 pub(crate) fn follow_download(&self, targets: &mut Targets) -> bool {
63 follow_id(
64 &StateOf(self),
65 &mut targets.bindings,
66 &targets.id.to_string(),
67 )
68 }
69
70 fn write_report(&self, targets: &Targets, report: &Report) -> Result<(), Failure> {
71 for (field, written) in report.writes() {
72 let bound = targets.bindings.iter().find(|(bound, _)| *bound == field);
73 if let Some((_, location)) = bound {
74 match written {
75 Written::Int(number) => {
76 self.write_location(location, value::int_slot(number), &INT)?;
77 }
78 Written::Text(text) => {
79 self.write_location(location, value::string_slot(text), &STRING)?;
80 }
81 }
82 }
83 }
84 Ok(())
85 }
86}
87
88#[unsafe(no_mangle)]
95pub(crate) unsafe extern "C" fn nf_download(
96 context: *const Context,
97 desc: &DownloadDesc,
98 span: u32,
99) -> bool {
100 let context = unsafe { borrow_context(context) };
102 let bindings = if desc.len == 0 {
103 &[]
104 } else {
105 unsafe { slice::from_raw_parts(desc.bindings, desc.len) }
107 };
108 let started = bindings
109 .iter()
110 .map(|binding| {
111 let place = unsafe { Location::from_place(&*binding.place) }?;
113 Ok((binding.field, place))
114 })
115 .collect::<Result<Vec<_>, Failure>>()
116 .and_then(|bindings| {
117 let id = DownloadId::next();
118 let targets = Targets { id, bindings, span };
119 context.write_report(&targets, &Report::Started(id))?;
120 Ok(targets)
121 });
122 match started {
123 Ok(targets) => {
124 context.request_download(Request {
125 id: targets.id,
126 url: value::as_str(desc.url).to_owned(),
127 folder: value::as_str(desc.folder).to_owned(),
128 name: value::as_str(desc.name).to_owned(),
129 connections: desc.connections,
130 headers: value::as_list(desc.headers)
131 .items
132 .iter()
133 .map(|header| value::as_str(*header).to_owned())
134 .collect(),
135 retries: desc.retries,
136 targets,
137 });
138 true
139 }
140 Err(failure) => {
141 context.fail(failure, span);
142 false
143 }
144 }
145}
146
147#[unsafe(no_mangle)]
152pub(crate) unsafe extern "C" fn nf_fail_download_into_variable(context: *const Context, span: u32) {
153 unsafe { borrow_context(context) }.fail(Failure::DownloadIntoVariable, span);
155}
156
157#[unsafe(no_mangle)]
163pub(crate) unsafe extern "C" fn nf_download_command(
164 context: *const Context,
165 command: DownloadCommand,
166 id: Slot,
167) {
168 let context = unsafe { borrow_context(context) };
170 context.request_download_command(command, value::as_str(id).to_owned());
171}
172
173#[unsafe(no_mangle)]
175pub(crate) extern "C" fn nf_format_bytes(bytes: i64) -> Slot {
176 value::string_slot(nifty_download::format_bytes(bytes))
177}