Skip to main content

data_for_seo/entity/
mod.rs

1//! Response and data models, organized by API domain.
2
3mod ai_optimization;
4mod app_data;
5mod appendix;
6mod backlinks;
7mod business_data;
8mod content_analysis;
9mod content_generation;
10mod dataforseo_labs;
11mod domain_analytics;
12mod keywords_data;
13mod merchant;
14mod on_page;
15mod serp;
16
17pub use ai_optimization::*;
18pub use app_data::*;
19pub use appendix::*;
20pub use backlinks::*;
21pub use business_data::*;
22pub use content_analysis::*;
23pub use content_generation::*;
24pub use dataforseo_labs::*;
25pub use domain_analytics::*;
26pub use keywords_data::*;
27pub use merchant::*;
28pub use on_page::*;
29use serde::{Deserialize, Serialize};
30use serde_json::Value;
31pub use serp::*;
32
33/// Top-level response envelope shared by every DataForSEO endpoint.
34#[derive(Debug, Default, Serialize, Deserialize, Clone)]
35pub struct DataForSeoApiResponseData<R> {
36    /// Current API version.
37    pub version: String,
38    /// General status code of the request; `20000` means "Ok".
39    /// See <https://docs.dataforseo.com/v3/appendix/errors/>.
40    pub status_code: i32,
41    /// Informational message accompanying `status_code`.
42    pub status_message: String,
43    /// Total execution time.
44    pub time: String,
45    /// Total cost of the request in USD.
46    pub cost: f64,
47    /// Number of tasks in the `tasks` array.
48    pub tasks_count: i32,
49    /// Number of tasks that returned an error.
50    pub tasks_error: i32,
51    /// Echo of the `limit` request parameter, where applicable.
52    pub limit: Option<i32>,
53    /// Echo of the `offset` request parameter, where applicable.
54    pub offset: Option<i32>,
55    /// Echo of the `sort` request parameter, where applicable.
56    pub sort: Option<String>,
57    /// Echo of the `include_metadata` request parameter, where applicable.
58    pub include_metadata: Option<bool>,
59    /// Tasks executed within this request.
60    pub tasks: Option<Vec<DataForSeoApiTask<R>>>,
61}
62
63impl<R> DataForSeoApiResponseData<R> {
64    /// All tasks in the response (empty slice when the API returned none).
65    pub fn tasks(&self) -> &[DataForSeoApiTask<R>] {
66        self.tasks.as_deref().unwrap_or_default()
67    }
68
69    /// Consumes the response and returns its tasks.
70    pub fn into_tasks(self) -> Vec<DataForSeoApiTask<R>> {
71        self.tasks.unwrap_or_default()
72    }
73
74    /// Iterates over every result of every task.
75    pub fn results(&self) -> impl Iterator<Item = &R> {
76        self.tasks().iter().flat_map(|task| task.results().iter())
77    }
78
79    /// Consumes the response and returns every result of every task.
80    pub fn into_results(self) -> Vec<R> {
81        self.into_tasks()
82            .into_iter()
83            .flat_map(|task| task.result.unwrap_or_default())
84            .collect()
85    }
86
87    /// First result across all tasks, if any.
88    pub fn first_result(&self) -> Option<&R> {
89        self.results().next()
90    }
91}
92
93/// A single task inside a [`DataForSeoApiResponseData`] envelope.
94#[derive(Debug, Default, Serialize, Deserialize, Clone)]
95pub struct DataForSeoApiTask<R> {
96    /// Unique task identifier in UUID format.
97    pub id: String,
98    /// Task status code; `20000` means "Ok".
99    /// See <https://docs.dataforseo.com/v3/appendix/errors/>.
100    pub status_code: i32,
101    /// Informational message accompanying `status_code`.
102    pub status_message: String,
103    /// Execution time of this task.
104    pub time: String,
105    /// Cost of this task in USD.
106    pub cost: f64,
107    /// URL path segments of the executed function (e.g. `["v3", "serp", ...]`).
108    pub path: Vec<String>,
109    /// Echo of the parameters submitted when the task was created.
110    pub data: Value,
111    /// Results produced by this task.
112    pub result: Option<Vec<R>>,
113}
114
115impl<R> DataForSeoApiTask<R> {
116    /// Results of this task (empty slice when the API returned none).
117    pub fn results(&self) -> &[R] {
118        self.result.as_deref().unwrap_or_default()
119    }
120
121    /// `true` when the task finished successfully (status code `20000`..`29999`).
122    pub fn is_success(&self) -> bool {
123        matches!(self.task_status(), TaskStatus::RequestSuccess(_))
124    }
125
126    /// Classifies the task's `status_code`.
127    /// See <https://docs.dataforseo.com/v3/appendix/errors/>.
128    pub fn task_status(&self) -> TaskStatus {
129        match self.status_code {
130            20000..=29999 => TaskStatus::RequestSuccess(self.status_code),
131            40601 | 40602 => TaskStatus::TaskWaiting(self.status_code),
132            40401 => TaskStatus::TaskNotFound(self.status_code),
133            40202 => TaskStatus::RateLimit(self.status_code),
134            other => TaskStatus::Other(other),
135        }
136    }
137}
138
139/// Classification of a task-level `status_code`.
140#[derive(Debug, Clone)]
141pub enum TaskStatus {
142    /// The task completed successfully (`20000`..`29999`).
143    RequestSuccess(i32),
144    /// The task was created but is not finished yet (`40601`, `40602`).
145    TaskWaiting(i32),
146    /// No task with the given id was found (`40401`).
147    TaskNotFound(i32),
148    /// The rate limit was exceeded (`40202`).
149    RateLimit(i32),
150    /// Any other status code.
151    Other(i32),
152}
153
154/// Common SERP task result wrapper: metadata about the search plus its `items`.
155#[derive(Debug, Default, Serialize, Deserialize, Clone)]
156pub struct SerpApiTaskResult<T> {
157    /// Keyword the search was performed for, UTF-8 decoded.
158    pub keyword: String,
159    /// Search engine type (e.g. `organic`, `maps`).
160    #[serde(rename = "type")]
161    pub search_engine_type: Option<String>,
162    /// Search engine domain the request was made to (e.g. `google.co.jp`).
163    pub se_domain: Option<String>,
164    /// Location code used in the task.
165    pub location_code: i32,
166    /// Language code used in the task.
167    pub language_code: Option<String>,
168    /// Direct URL of the search query.
169    pub check_url: Option<String>,
170    /// Date and time when the result was received, UTC.
171    pub datetime: Option<String>,
172    /// Autocorrection info applied by the search engine, if any.
173    pub spell: Option<Value>,
174    /// Search refinement chips shown in the SERP, if any.
175    pub refinement_chips: Option<SerpApiElementRefinementChips>,
176    /// Element types present in `items` (e.g. `organic`, `people_also_ask`).
177    pub item_types: Option<Vec<String>>,
178    /// Total number of results the search engine reported.
179    pub se_results_count: Option<i64>,
180    /// Number of items returned in `items`.
181    pub items_count: Option<i64>,
182    /// Elements of the search results page.
183    pub items: Option<Vec<T>>,
184}
185
186impl<T> SerpApiTaskResult<T> {
187    /// Items of this result (empty slice when the API returned none).
188    pub fn items(&self) -> &[T] {
189        self.items.as_deref().unwrap_or_default()
190    }
191}