11package generator
22
33import (
4+ "bufio"
5+ "os"
6+ "path"
47 "path/filepath"
58 "strings"
69
@@ -126,48 +129,47 @@ func mkdir(ctx *ctx.ProjectContext, proto parser.Proto, conf *conf.Config, c *ZR
126129 }
127130 inner [etc ] = Dir {
128131 Filename : etcDir ,
129- Package : filepath . ToSlash ( filepath . Join ( ctx . Path , strings . TrimPrefix ( etcDir , ctx . Dir )) ),
132+ Package : resolveDirPackage ( ctx , etcDir , "" ),
130133 Base : filepath .Base (etcDir ),
131134 GetChildPackage : func (childPath string ) (string , error ) {
132135 return getChildPackage (etcDir , childPath )
133136 },
134137 }
135138 inner [internal ] = Dir {
136139 Filename : internalDir ,
137- Package : filepath .ToSlash (filepath .Join (ctx .Path ,
138- strings .TrimPrefix (internalDir , ctx .Dir ))),
139- Base : filepath .Base (internalDir ),
140+ Package : resolveDirPackage (ctx , internalDir , "" ),
141+ Base : filepath .Base (internalDir ),
140142 GetChildPackage : func (childPath string ) (string , error ) {
141143 return getChildPackage (internalDir , childPath )
142144 },
143145 }
144146 inner [config ] = Dir {
145147 Filename : configDir ,
146- Package : filepath . ToSlash ( filepath . Join ( ctx . Path , strings . TrimPrefix ( configDir , ctx . Dir )) ),
148+ Package : resolveDirPackage ( ctx , configDir , "" ),
147149 Base : filepath .Base (configDir ),
148150 GetChildPackage : func (childPath string ) (string , error ) {
149151 return getChildPackage (configDir , childPath )
150152 },
151153 }
152154 inner [logic ] = Dir {
153155 Filename : logicDir ,
154- Package : filepath . ToSlash ( filepath . Join ( ctx . Path , strings . TrimPrefix ( logicDir , ctx . Dir )) ),
156+ Package : resolveDirPackage ( ctx , logicDir , "" ),
155157 Base : filepath .Base (logicDir ),
156158 GetChildPackage : func (childPath string ) (string , error ) {
157159 return getChildPackage (logicDir , childPath )
158160 },
159161 }
160162 inner [server ] = Dir {
161163 Filename : serverDir ,
162- Package : filepath . ToSlash ( filepath . Join ( ctx . Path , strings . TrimPrefix ( serverDir , ctx . Dir )) ),
164+ Package : resolveDirPackage ( ctx , serverDir , "" ),
163165 Base : filepath .Base (serverDir ),
164166 GetChildPackage : func (childPath string ) (string , error ) {
165167 return getChildPackage (serverDir , childPath )
166168 },
167169 }
168170 inner [svc ] = Dir {
169171 Filename : svcDir ,
170- Package : filepath . ToSlash ( filepath . Join ( ctx . Path , strings . TrimPrefix ( svcDir , ctx . Dir )) ),
172+ Package : resolveDirPackage ( ctx , svcDir , "" ),
171173 Base : filepath .Base (svcDir ),
172174 GetChildPackage : func (childPath string ) (string , error ) {
173175 return getChildPackage (svcDir , childPath )
@@ -176,7 +178,7 @@ func mkdir(ctx *ctx.ProjectContext, proto parser.Proto, conf *conf.Config, c *ZR
176178
177179 inner [pb ] = Dir {
178180 Filename : pbDir ,
179- Package : filepath . ToSlash ( filepath . Join ( ctx . Path , strings . TrimPrefix ( pbDir , ctx . Dir )) ),
181+ Package : resolveDirPackage ( ctx , pbDir , proto . GoPackage ),
180182 Base : filepath .Base (pbDir ),
181183 GetChildPackage : func (childPath string ) (string , error ) {
182184 return getChildPackage (pbDir , childPath )
@@ -185,9 +187,8 @@ func mkdir(ctx *ctx.ProjectContext, proto parser.Proto, conf *conf.Config, c *ZR
185187
186188 inner [protoGo ] = Dir {
187189 Filename : protoGoDir ,
188- Package : filepath .ToSlash (filepath .Join (ctx .Path ,
189- strings .TrimPrefix (protoGoDir , ctx .Dir ))),
190- Base : filepath .Base (protoGoDir ),
190+ Package : resolveDirPackage (ctx , protoGoDir , proto .GoPackage ),
191+ Base : filepath .Base (protoGoDir ),
191192 GetChildPackage : func (childPath string ) (string , error ) {
192193 return getChildPackage (protoGoDir , childPath )
193194 },
@@ -212,16 +213,100 @@ func mkdir(ctx *ctx.ProjectContext, proto parser.Proto, conf *conf.Config, c *ZR
212213func (d * defaultDirContext ) SetPbDir (pbDir , grpcDir string ) {
213214 d .inner [pb ] = Dir {
214215 Filename : pbDir ,
215- Package : filepath . ToSlash ( filepath . Join ( d .ctx . Path , strings . TrimPrefix ( pbDir , d . ctx . Dir )) ),
216+ Package : resolveDirPackage ( d .ctx , pbDir , "" ),
216217 Base : filepath .Base (pbDir ),
217218 }
218219
219220 d .inner [protoGo ] = Dir {
220221 Filename : grpcDir ,
221- Package : filepath .ToSlash (filepath .Join (d .ctx .Path ,
222- strings .TrimPrefix (grpcDir , d .ctx .Dir ))),
223- Base : filepath .Base (grpcDir ),
222+ Package : resolveDirPackage (d .ctx , grpcDir , "" ),
223+ Base : filepath .Base (grpcDir ),
224+ }
225+ }
226+
227+ func resolveDirPackage (project * ctx.ProjectContext , dir , fallback string ) string {
228+ dir = filepath .Clean (dir )
229+ projectDir := filepath .Clean (project .Dir )
230+
231+ if rel , ok := relativeImportPath (projectDir , dir ); ok {
232+ return joinImportPath (project .Path , rel )
233+ }
234+
235+ if modulePath , moduleDir , ok := resolveModuleForDir (dir ); ok {
236+ if rel , ok := relativeImportPath (moduleDir , dir ); ok {
237+ return joinImportPath (modulePath , rel )
238+ }
239+ }
240+
241+ if isImportPath (fallback ) {
242+ return filepath .ToSlash (fallback )
243+ }
244+
245+ return joinImportPath (project .Path , strings .TrimPrefix (dir , projectDir ))
246+ }
247+
248+ func relativeImportPath (baseDir , targetDir string ) (string , bool ) {
249+ rel , err := filepath .Rel (baseDir , targetDir )
250+ if err != nil {
251+ return "" , false
252+ }
253+ if rel == "." {
254+ return "" , true
255+ }
256+ if rel == ".." || strings .HasPrefix (rel , ".." + string (filepath .Separator )) || filepath .IsAbs (rel ) {
257+ return "" , false
258+ }
259+
260+ return filepath .ToSlash (rel ), true
261+ }
262+
263+ func joinImportPath (basePath , rel string ) string {
264+ if rel == "" || rel == "." {
265+ return filepath .ToSlash (basePath )
224266 }
267+
268+ return path .Join (filepath .ToSlash (basePath ), filepath .ToSlash (rel ))
269+ }
270+
271+ func isImportPath (value string ) bool {
272+ return value != "" && ! strings .HasPrefix (value , "." ) && ! filepath .IsAbs (value )
273+ }
274+
275+ func resolveModuleForDir (dir string ) (modulePath , moduleDir string , ok bool ) {
276+ current := filepath .Clean (dir )
277+ for {
278+ goMod := filepath .Join (current , "go.mod" )
279+ if info , err := os .Stat (goMod ); err == nil && ! info .IsDir () {
280+ modulePath , err := readModulePath (goMod )
281+ if err == nil && modulePath != "" {
282+ return modulePath , current , true
283+ }
284+ }
285+
286+ parent := filepath .Dir (current )
287+ if parent == current {
288+ return "" , "" , false
289+ }
290+ current = parent
291+ }
292+ }
293+
294+ func readModulePath (goMod string ) (string , error ) {
295+ file , err := os .Open (goMod )
296+ if err != nil {
297+ return "" , err
298+ }
299+ defer file .Close ()
300+
301+ scanner := bufio .NewScanner (file )
302+ for scanner .Scan () {
303+ line := strings .TrimSpace (scanner .Text ())
304+ if strings .HasPrefix (line , "module " ) {
305+ return strings .TrimSpace (strings .TrimPrefix (line , "module " )), nil
306+ }
307+ }
308+
309+ return "" , scanner .Err ()
225310}
226311
227312func (d * defaultDirContext ) GetCall () Dir {
0 commit comments